gdk: add MMX accelerated GdkRGB gray conversion paths

This commit is contained in:
Daemonratte 2026-09-01 18:25:28 +02:00
commit 65bc2e7b4b
4 changed files with 424 additions and 0 deletions

View file

@ -10,6 +10,11 @@
#include "gdkpixelops.h"
#include "gdkimage.h"
#include "gdkrgb.h"
typedef struct _GdkRgbInfo GdkRgbInfo;
#ifndef __i386__
#error "gdkpixelops-x86-mmx.c requires a 32-bit x86 target"
#endif
@ -299,3 +304,347 @@ DEFINE_RGB16_MMX_COMPOSITOR (
pack_565)
#undef DEFINE_RGB16_MMX_COMPOSITOR
/*
* GdkRGB Gray conversion MMX kernels.
*/
static __m64
gdk_rgb_gray_mmx_load4 (const guchar *p)
{
guint32 v;
memcpy (&v, p, sizeof (v));
return _mm_cvtsi32_si64 ((gint) v);
}
static __m64
gdk_rgb_gray_mmx_load2 (const guchar *p)
{
guint16 v;
memcpy (&v, p, sizeof (v));
return _mm_cvtsi32_si64 ((gint) v);
}
static void
gdk_rgb_gray_mmx_store8 (guchar *p,
__m64 v)
{
memcpy (p, &v, sizeof (v));
}
static __m64
gdk_rgb_gray_mmx_555 (__m64 b)
{
const __m64 zero = _mm_setzero_si64 ();
__m64 g = _mm_unpacklo_pi8 (b, zero);
__m64 v = _mm_srli_pi16 (g, 3);
return _mm_or_si64 (
_mm_or_si64 (
_mm_slli_pi16 (v, 10),
_mm_slli_pi16 (v, 5)),
v);
}
static __m64
gdk_rgb_gray_mmx_565 (__m64 b)
{
const __m64 zero = _mm_setzero_si64 ();
__m64 g = _mm_unpacklo_pi8 (b, zero);
__m64 v5 = _mm_srli_pi16 (g, 3);
__m64 v6 = _mm_srli_pi16 (g, 2);
return _mm_or_si64 (
_mm_or_si64 (
_mm_slli_pi16 (v5, 11),
_mm_slli_pi16 (v6, 5)),
v5);
}
static __m64
gdk_rgb_gray_mmx_swap16 (__m64 v)
{
return _mm_or_si64 (
_mm_slli_pi16 (v, 8),
_mm_srli_pi16 (v, 8));
}
static guint16
gdk_rgb_gray_tail_555 (guint g)
{
return (guint16)
(((g & 0xf8) << 7) |
((g & 0xf8) << 2) |
(g >> 3));
}
static guint16
gdk_rgb_gray_tail_565 (guint g)
{
return (guint16)
(((g & 0xf8) << 8) |
((g & 0xfc) << 3) |
(g >> 3));
}
static guint16
gdk_rgb_gray_tail_swap16 (guint16 v)
{
return (guint16) ((v << 8) | (v >> 8));
}
#define DEFINE_GDK_RGB_GRAY16_MMX(name, vector_value, tail_value) \
G_GNUC_INTERNAL void \
name (GdkRgbInfo *image_info, \
GdkImage *image, \
gint x0, \
gint y0, \
gint width, \
gint height, \
const guchar *buf, \
gint rowstride, \
gint x_align, \
gint y_align, \
GdkRgbCmap *cmap) \
{ \
const guchar *src = buf; \
guchar *dst = ((guchar *) image->mem) + \
y0 * image->bpl + x0 * 2; \
gint y; \
\
(void) image_info; \
(void) x_align; \
(void) y_align; \
(void) cmap; \
\
for (y = 0; y < height; y++) \
{ \
const guchar *p = src; \
guchar *q = dst; \
gint x = 0; \
\
for (; x + 4 <= width; x += 4) \
{ \
__m64 out = (vector_value); \
\
gdk_rgb_gray_mmx_store8 (q, out); \
\
p += 4; \
q += 8; \
} \
\
for (; x < width; x++) \
{ \
guint16 out = (tail_value); \
\
memcpy (q, &out, sizeof (out)); \
\
p++; \
q += 2; \
} \
\
src += rowstride; \
dst += image->bpl; \
} \
\
if (width >= 4 && height > 0) \
_mm_empty (); \
}
DEFINE_GDK_RGB_GRAY16_MMX (
_gdk_rgb_convert_gray_555_mmx,
gdk_rgb_gray_mmx_555 (
gdk_rgb_gray_mmx_load4 (p)),
gdk_rgb_gray_tail_555 (*p))
DEFINE_GDK_RGB_GRAY16_MMX (
_gdk_rgb_convert_gray_555_br_mmx,
gdk_rgb_gray_mmx_swap16 (
gdk_rgb_gray_mmx_555 (
gdk_rgb_gray_mmx_load4 (p))),
gdk_rgb_gray_tail_swap16 (
gdk_rgb_gray_tail_555 (*p)))
DEFINE_GDK_RGB_GRAY16_MMX (
_gdk_rgb_convert_gray_565_mmx,
gdk_rgb_gray_mmx_565 (
gdk_rgb_gray_mmx_load4 (p)),
gdk_rgb_gray_tail_565 (*p))
DEFINE_GDK_RGB_GRAY16_MMX (
_gdk_rgb_convert_gray_565_br_mmx,
gdk_rgb_gray_mmx_swap16 (
gdk_rgb_gray_mmx_565 (
gdk_rgb_gray_mmx_load4 (p))),
gdk_rgb_gray_tail_swap16 (
gdk_rgb_gray_tail_565 (*p)))
#undef DEFINE_GDK_RGB_GRAY16_MMX
/*
* One shared 101010 implementation.
* The two GdkRgbConvFunc wrappers differ only in the alpha mask.
*/
static __m64
gdk_rgb_gray_mmx_dwords2 (const guchar *p)
{
const __m64 zero = _mm_setzero_si64 ();
__m64 b = gdk_rgb_gray_mmx_load2 (p);
__m64 w = _mm_unpacklo_pi8 (b, zero);
return _mm_unpacklo_pi16 (w, zero);
}
static void
gdk_rgb_gray_101010_lsb_mmx_impl (GdkImage *image,
gint x0,
gint y0,
gint width,
gint height,
const guchar *buf,
gint rowstride,
guint32 alpha)
{
if (width < 4)
{
const guchar *tiny_src = buf;
guchar *tiny_dst =
((guchar *) image->mem) +
y0 * image->bpl +
x0 * 4;
gint tiny_y;
for (tiny_y = 0; tiny_y < height; tiny_y++)
{
const guchar *p = tiny_src;
guchar *q = tiny_dst;
gint x;
for (x = 0; x < width; x++)
{
guint32 g = *p++;
guint32 g10 =
(g << 2) | (g >> 6);
guint32 out =
alpha |
(g10 << 20) |
(g10 << 10) |
g10;
memcpy (q, &out, sizeof (out));
q += 4;
}
tiny_src += rowstride;
tiny_dst += image->bpl;
}
return;
}
const __m64 alpha2 =
_mm_set_pi32 ((gint) alpha, (gint) alpha);
const guchar *src = buf;
guchar *dst =
((guchar *) image->mem) +
y0 * image->bpl +
x0 * 4;
gint y;
for (y = 0; y < height; y++)
{
const guchar *p = src;
guchar *q = dst;
gint x = 0;
for (; x + 2 <= width; x += 2)
{
__m64 g =
gdk_rgb_gray_mmx_dwords2 (p);
__m64 g10 =
_mm_or_si64 (
_mm_slli_pi32 (g, 2),
_mm_srli_pi32 (g, 6));
__m64 out =
_mm_or_si64 (
alpha2,
_mm_or_si64 (
g10,
_mm_or_si64 (
_mm_slli_pi32 (g10, 10),
_mm_slli_pi32 (g10, 20))));
gdk_rgb_gray_mmx_store8 (q, out);
p += 2;
q += 8;
}
for (; x < width; x++)
{
guint32 g = *p++;
guint32 g10 =
(g << 2) | (g >> 6);
guint32 out =
alpha |
(g10 << 20) |
(g10 << 10) |
g10;
memcpy (q, &out, sizeof (out));
q += 4;
}
src += rowstride;
dst += image->bpl;
}
if (width >= 2 && height > 0)
_mm_empty ();
}
#define DEFINE_GDK_RGB_GRAY101010_MMX(name, alpha) \
G_GNUC_INTERNAL void \
name (GdkRgbInfo *image_info, \
GdkImage *image, \
gint x0, \
gint y0, \
gint width, \
gint height, \
const guchar *buf, \
gint rowstride, \
gint x_align, \
gint y_align, \
GdkRgbCmap *cmap) \
{ \
(void) image_info; \
(void) x_align; \
(void) y_align; \
(void) cmap; \
\
gdk_rgb_gray_101010_lsb_mmx_impl ( \
image, x0, y0, \
width, height, \
buf, rowstride, \
alpha); \
}
DEFINE_GDK_RGB_GRAY101010_MMX (
_gdk_rgb_convert_gray_101010_lsb_30_mmx,
0)
DEFINE_GDK_RGB_GRAY101010_MMX (
_gdk_rgb_convert_gray_101010_lsb_32_mmx,
0xc0000000U)
#undef DEFINE_GDK_RGB_GRAY101010_MMX

View file

@ -40,6 +40,14 @@ _gdk_pixel_ops_composite_565_mmx (guchar *src_buf,
gint width,
gint height);
static gboolean gdk_pixel_ops_mmx_available;
G_GNUC_INTERNAL gboolean
_gdk_pixel_ops_have_mmx (void)
{
return gdk_pixel_ops_mmx_available;
}
G_GNUC_INTERNAL void
_gdk_pixel_ops_init (void)
{
@ -51,6 +59,7 @@ _gdk_pixel_ops_init (void)
if (__get_cpuid (1, &eax, &ebx, &ecx, &edx) &&
(edx & bit_MMX) != 0)
{
gdk_pixel_ops_mmx_available = TRUE;
_gdk_pixel_ops.composite_0888 =
_gdk_pixel_ops_composite_0888_mmx;
_gdk_pixel_ops.composite_555 =

View file

@ -29,8 +29,10 @@ extern G_GNUC_INTERNAL GdkPixelOps _gdk_pixel_ops;
#ifdef HAVE_GDK_PIXELOPS_INIT
G_GNUC_INTERNAL void _gdk_pixel_ops_init (void);
G_GNUC_INTERNAL gboolean _gdk_pixel_ops_have_mmx (void);
#else
#define _gdk_pixel_ops_init() ((void) 0)
#define _gdk_pixel_ops_have_mmx() FALSE
#endif
G_END_DECLS

View file

@ -39,6 +39,7 @@
#include "gdkinternals.h" /* _gdk_windowing_get_bits_for_depth() */
#include "gdkrgb.h"
#include "gdkpixelops.h"
#include "gdkscreen.h"
#include "gdkalias.h"
#include <glib/gprintf.h>
@ -71,6 +72,25 @@ typedef void (*GdkRgbConvFunc) (GdkRgbInfo *image_info, GdkImage *image,
gint x_align, gint y_align,
GdkRgbCmap *cmap);
#ifdef HAVE_GDK_PIXELOPS_INIT
#define DECLARE_GDK_RGB_GRAY_MMX(name) \
G_GNUC_INTERNAL void \
name (GdkRgbInfo *, GdkImage *, \
gint, gint, gint, gint, \
const guchar *, gint, \
gint, gint, GdkRgbCmap *);
DECLARE_GDK_RGB_GRAY_MMX (_gdk_rgb_convert_gray_555_mmx)
DECLARE_GDK_RGB_GRAY_MMX (_gdk_rgb_convert_gray_555_br_mmx)
DECLARE_GDK_RGB_GRAY_MMX (_gdk_rgb_convert_gray_565_mmx)
DECLARE_GDK_RGB_GRAY_MMX (_gdk_rgb_convert_gray_565_br_mmx)
DECLARE_GDK_RGB_GRAY_MMX (_gdk_rgb_convert_gray_101010_lsb_30_mmx)
DECLARE_GDK_RGB_GRAY_MMX (_gdk_rgb_convert_gray_101010_lsb_32_mmx)
#undef DECLARE_GDK_RGB_GRAY_MMX
#endif
#define STAGE_ROWSTRIDE (GDK_SCRATCH_IMAGE_WIDTH * 3)
#define GDK_RGB_STAGE_ROWS 4
@ -4112,6 +4132,50 @@ gdk_rgb_select_conv (GdkRgbInfo *image_info)
break;
}
#ifdef HAVE_GDK_PIXELOPS_INIT
if (_gdk_pixel_ops_have_mmx ())
{
switch (format)
{
case GDK_RGB_FORMAT_555:
conv_gray =
_gdk_rgb_convert_gray_555_mmx;
conv_gray_d = conv_gray;
break;
case GDK_RGB_FORMAT_555_BR:
conv_gray =
_gdk_rgb_convert_gray_555_br_mmx;
conv_gray_d = conv_gray;
break;
case GDK_RGB_FORMAT_565:
conv_gray =
_gdk_rgb_convert_gray_565_mmx;
break;
case GDK_RGB_FORMAT_565_BR:
conv_gray =
_gdk_rgb_convert_gray_565_br_mmx;
conv_gray_d = conv_gray;
break;
case GDK_RGB_FORMAT_101010_R_LOW_LSB:
case GDK_RGB_FORMAT_101010_B_LOW_LSB:
conv_gray =
depth == 32
? _gdk_rgb_convert_gray_101010_lsb_32_mmx
: _gdk_rgb_convert_gray_101010_lsb_30_mmx;
conv_gray_d = conv_gray;
break;
default:
break;
}
}
#endif
image_info->format = format;
image_info->conv = conv;