From 76f481eb7b5c1bca7f47b2ec244539a8147af5ee Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 10 Sep 2021 20:51:11 -0400 Subject: [PATCH 1/3] Stop using config.h.meson It isn't necessary and makes us miss defines when we forget to update it. --- config.h.meson | 286 ------------------------------------------------- meson.build | 8 +- 2 files changed, 1 insertion(+), 293 deletions(-) delete mode 100644 config.h.meson diff --git a/config.h.meson b/config.h.meson deleted file mode 100644 index f4f55122ee..0000000000 --- a/config.h.meson +++ /dev/null @@ -1,286 +0,0 @@ -/* always defined to indicate that i18n is enabled */ -#define ENABLE_NLS 1 - -/* Use structured logging */ -#define G_LOG_STRUCTURED 1 - -/* The prefix for our gettext translation domains. */ -#mesondefine GETTEXT_PACKAGE - -/* Disable deprecation warnings from glib */ -#mesondefine GLIB_DISABLE_DEPRECATION_WARNINGS - -/* Define the location where the catalogs will be installed */ -#mesondefine GTK_LOCALEDIR - -/* Define to 1 if you have the `bind_textdomain_codeset' function. */ -#mesondefine HAVE_BIND_TEXTDOMAIN_CODESET - -/* Have the cloudproviders library */ -#mesondefine HAVE_CLOUDPROVIDERS - -/* define if we have colord */ -#mesondefine HAVE_COLORD - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_CRT_EXTERNS_H - -/* Define to 1 if you have the `dcgettext' function. */ -#mesondefine HAVE_DCGETTEXT - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_DLFCN_H - -/* Have the ffmpeg library */ -#mesondefine HAVE_FFMPEG - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_FTW_H - -/* Define to 1 if you have the `getpagesize' function. */ -#mesondefine HAVE_GETPAGESIZE - -/* Define to 1 if you have the `getresuid' function. */ -#mesondefine HAVE_GETRESUID - -/* Define if gio-unix is available */ -#mesondefine HAVE_GIO_UNIX - -/* Define if GStreamer support is available */ -#mesondefine HAVE_GSTREAMER - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_INTTYPES_H - -/* Define to 1 if the system has the type `IPrintDialogCallback'. */ -#mesondefine HAVE_IPRINTDIALOGCALLBACK - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_LOCALE_H - -/* Define to 1 if you have the `lstat' function. */ -#mesondefine HAVE_LSTAT - -/* Define to 1 if you have the `mallinfo' function. */ -#mesondefine HAVE_MALLINFO - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_MEMORY_H - -/* Define to 1 if you have the `mkstemp' function. */ -#mesondefine HAVE_MKSTEMP - -/* Define to 1 if you have the `mlock` function. */ -#mesondefine HAVE_MLOCK - -/* Define to 1 if you have a working `mmap' system call. */ -#mesondefine HAVE_MMAP - -/* Define to 1 if you have a working `madvise' system call. */ -#mesondefine HAVE_MADVISE - -/* Define to 1 if you have the `posix_fallocate' function. */ -#mesondefine HAVE_POSIX_FALLOCATE - -/* Have the Xrandr extension library */ -#mesondefine HAVE_RANDR - -/* Have the Xrandr 1.5 extension library */ -#mesondefine HAVE_RANDR15 - -/* Define to 1 if you have the `sincos' function. */ -#mesondefine HAVE_SINCOS - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_STDINT_H - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_STDLIB_H - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_STRINGS_H - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_STRING_H - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_SYS_MMAN_H - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_SYS_PARAM_H - -/* Have the sysprof-capture library */ -#mesondefine HAVE_SYSPROF - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_SYS_STAT_H - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_SYS_TIME_H - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_SYS_TYPES_H - -/* Define to 1 if you have the header file. */ -#mesondefine HAVE_UNISTD_H - -/* Have the Xcursor library */ -#mesondefine HAVE_XCURSOR - -/* Have the XDAMAGE X extension */ -#mesondefine HAVE_XDAMAGE - -/* Have the XFIXES X extension */ -#mesondefine HAVE_XFIXES - -/* Define to 1 if XFree Xinerama is available */ -#mesondefine HAVE_XFREE_XINERAMA - -/* Have XGenericEvent */ -#mesondefine HAVE_XGENERICEVENTS - -/* Define to use XKB extension */ -#mesondefine HAVE_XKB - -/* Have the SYNC extension library */ -#mesondefine HAVE_XSYNC - -/* Define to 1 if you have the `_lock_file' function */ -#mesondefine HAVE__LOCK_FILE - -/* Define to 1 if you have the `flockfile' function */ -#mesondefine HAVE_FLOCKFILE - -/* Define if _NL_MEASUREMENT_MEASUREMENT is available */ -#mesondefine HAVE__NL_MEASUREMENT_MEASUREMENT - -/* Define if _NL_PAPER_HEIGHT is available */ -#mesondefine HAVE__NL_PAPER_HEIGHT - -/* Define if _NL_PAPER_WIDTH is available */ -#mesondefine HAVE__NL_PAPER_WIDTH - -/* Define if _NL_TIME_FIRST_WEEKDAY is available */ -#mesondefine HAVE__NL_TIME_FIRST_WEEKDAY - -/* Define to the sub-directory where libtool stores uninstalled libraries. */ -#mesondefine LT_OBJDIR - -/* Define to 1 if your C compiler doesn't accept -c and -o together. */ -#mesondefine NO_MINUS_C_MINUS_O - -/* Define to the address where bug reports for this package should be sent. */ -#mesondefine PACKAGE_BUGREPORT - -/* Define to the full name of this package. */ -#mesondefine PACKAGE_NAME - -/* Define to the full name and version of this package. */ -#mesondefine PACKAGE_STRING - -/* Define to the one symbol short name of this package. */ -#mesondefine PACKAGE_TARNAME - -/* Define to the home page for this package. */ -#mesondefine PACKAGE_URL - -/* Define to the version of this package. */ -#mesondefine PACKAGE_VERSION - -/* Use NSBundle functions to determine load paths for libraries, translations, - etc. */ -#mesondefine QUARTZ_RELOCATION - -/* Define to 1 if you have the ANSI C header files. */ -#mesondefine STDC_HEADERS - -/* Enable extensions on AIX 3, Interix. */ -#ifndef _ALL_SOURCE -# undef _ALL_SOURCE -#endif -/* Enable GNU extensions on systems that have them. */ -#ifndef _GNU_SOURCE -# undef _GNU_SOURCE -#endif -/* Enable threading extensions on Solaris. */ -#ifndef _POSIX_PTHREAD_SEMANTICS -# undef _POSIX_PTHREAD_SEMANTICS -#endif -/* Enable extensions on HP NonStop. */ -#ifndef _TANDEM_SOURCE -# undef _TANDEM_SOURCE -#endif -/* Enable general extensions on Solaris. */ -#ifndef __EXTENSIONS__ -# undef __EXTENSIONS__ -#endif - - -/* Define to 1 if XInput 2.2 is available */ -#mesondefine XINPUT_2_2 - -/* Define to 1 if the X Window System is missing or not being used. */ -#mesondefine X_DISPLAY_MISSING - -/* Enable large inode numbers on Mac OS X 10.5. */ -#ifndef _DARWIN_USE_64_BIT_INODE -# define _DARWIN_USE_64_BIT_INODE 1 -#endif - -/* Number of bits in a file offset, on hosts where this is settable. */ -#mesondefine _FILE_OFFSET_BITS - -/* defines how to decorate public symbols while building */ -#mesondefine _GDK_EXTERN - -/* Define for large files, on AIX-style hosts. */ -#mesondefine _LARGE_FILES - -/* Define to 1 if on MINIX. */ -#mesondefine _MINIX - -/* Define to 2 if the system does not provide POSIX.1 features except with - this defined. */ -#mesondefine _POSIX_1_SOURCE - -/* Define to 1 if you need to in order for `stat' and other things to work. */ -#mesondefine _POSIX_SOURCE - -/* Define to `int' if doesn't define. */ -#mesondefine gid_t - -/* Define to `int' if doesn't define. */ -#mesondefine uid_t - -/* Define to 1 if linux/memfd.h exists */ -#mesondefine HAVE_LINUX_MEMFD_H - -#mesondefine HAVE_LINUX_INPUT_H - -#mesondefine HAVE_DEV_EVDEV_INPUT_H - -#mesondefine GTK_SYSCONFDIR - -#mesondefine GTK_LOCALEDIR - -#mesondefine GTK_DATADIR - -#mesondefine GTK_LIBDIR - -#mesondefine GTK_PRINT_BACKENDS - -#mesondefine HAVE_CAIRO_SCRIPT_INTERPRETER - -#mesondefine HAVE_HARFBUZZ - -#mesondefine HAVE_PANGOFT - -#mesondefine ISO_CODES_PREFIX - -/* Define if tracker3 is available */ -#mesondefine HAVE_TRACKER3 - -#mesondefine HAVE_F16C - -/* Does the OS support GDesktopAppInfo? */ -#mesondefine HAVE_DESKTOPAPPINFO diff --git a/meson.build b/meson.build index c0b5407a0d..070aa6a80d 100644 --- a/meson.build +++ b/meson.build @@ -150,11 +150,6 @@ cdata.set_quoted('GTK_DATADIR', gtk_datadir) cdata.set_quoted('GTK_LIBDIR', gtk_libdir) cdata.set_quoted('GTK_SYSCONFDIR', gtk_sysconfdir) cdata.set_quoted('GETTEXT_PACKAGE', 'gtk40') -cdata.set('GTK_MAJOR_VERSION', gtk_major_version) -cdata.set('GTK_MINOR_VERSION', gtk_minor_version) -cdata.set('GTK_MICRO_VERSION', gtk_micro_version) -cdata.set('GTK_BINARY_AGE', gtk_binary_age) -cdata.set('GTK_INTERFACE_AGE', gtk_interface_age) check_headers = [ 'crt/externs.h', @@ -747,8 +742,7 @@ if get_option('build-examples') endif # config.h -configure_file(input: 'config.h.meson', - output: 'config.h', +configure_file(output: 'config.h', configuration: cdata) # Requires From 155a4fac5cc668e3142ed62803bd38c80c1f38c2 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 10 Sep 2021 07:46:24 -0400 Subject: [PATCH 2/3] Add vectorized half-float conversion We can't make the -4 versions inline, since we use ifuncs for them, so make vectorized versions. Test included. --- gsk/ngl/fp16.c | 96 ++++++++++++++++++++++++++++++++++---- gsk/ngl/fp16i.c | 71 +++++++++++++++++++++++++++- gsk/ngl/fp16private.h | 24 ++++++++++ testsuite/gsk/half-float.c | 54 +++++++++++++++++---- 4 files changed, 225 insertions(+), 20 deletions(-) diff --git a/gsk/ngl/fp16.c b/gsk/ngl/fp16.c index a1ff1d1af9..2f71820ce4 100644 --- a/gsk/ngl/fp16.c +++ b/gsk/ngl/fp16.c @@ -37,7 +37,7 @@ as_float (const guint x) // IEEE-754 16-bit floating-point format (without infinity): 1-5-10 static inline float -half_to_float (const guint16 x) +half_to_float_one (const guint16 x) { const guint e = (x&0x7C00)>>10; // exponent const guint m = (x&0x03FF)<<13; // mantissa @@ -46,7 +46,7 @@ half_to_float (const guint16 x) } static inline guint16 -float_to_half (const float x) +float_to_half_one (const float x) { const guint b = as_uint(x)+0x00001000; // round-to-nearest-even const guint e = (b&0x7F800000)>>23; // exponent @@ -58,20 +58,38 @@ void float_to_half4_c (const float f[4], guint16 h[4]) { - h[0] = float_to_half (f[0]); - h[1] = float_to_half (f[1]); - h[2] = float_to_half (f[2]); - h[3] = float_to_half (f[3]); + h[0] = float_to_half_one (f[0]); + h[1] = float_to_half_one (f[1]); + h[2] = float_to_half_one (f[2]); + h[3] = float_to_half_one (f[3]); } void half_to_float4_c (const guint16 h[4], float f[4]) { - f[0] = half_to_float (h[0]); - f[1] = half_to_float (h[1]); - f[2] = half_to_float (h[2]); - f[3] = half_to_float (h[3]); + f[0] = half_to_float_one (h[0]); + f[1] = half_to_float_one (h[1]); + f[2] = half_to_float_one (h[2]); + f[3] = half_to_float_one (h[3]); +} + +void +float_to_half_c (const float *f, + guint16 *h, + int n) +{ + for (int i = 0; i < n; i++) + h[i] = float_to_half_one (f[i]); +} + +void +half_to_float_c (const guint16 *h, + float *f, + int n) +{ + for (int i = 0; i < n; i++) + f[i] = half_to_float_one (h[i]); } #ifdef HAVE_F16C @@ -122,10 +140,30 @@ half_to_float4 (const guint16 h[4], float f[4]) half_to_float4_c (h, f); } +void +float_to_half (const float *f, guint16 *h, int n) +{ + if (have_f16c_msvc ()) + float_to_half_f16c (f, h, n); + else + float_to_half4_c (f, h, n); +} + +void +half_to_float (const guint16 *h, float *f, int n) +{ + if (have_f16c_msvc ()) + half_to_float_f16c (h, f, n); + else + half_to_float_c (h, f, n); +} + #else void float_to_half4 (const float f[4], guint16 h[4]) __attribute__((ifunc ("resolve_float_to_half4"))); void half_to_float4 (const guint16 h[4], float f[4]) __attribute__((ifunc ("resolve_half_to_float4"))); +void float_to_half (const float *f, guint16 *h, int n) __attribute__((ifunc ("resolve_float_to_half"))); +void half_to_float (const guint16 *h, float *f, int n) __attribute__((ifunc ("resolve_half_to_float"))); static void * resolve_float_to_half4 (void) @@ -147,6 +185,26 @@ resolve_half_to_float4 (void) return half_to_float4_c; } +static void * +resolve_float_to_half (void) +{ + __builtin_cpu_init (); + if (__builtin_cpu_supports ("f16c")) + return float_to_half_f16c; + else + return float_to_half_c; +} + +static void * +resolve_half_to_float (void) +{ + __builtin_cpu_init (); + if (__builtin_cpu_supports ("f16c")) + return half_to_float_f16c; + else + return half_to_float_c; +} + #endif #else /* ! HAVE_F16C */ @@ -168,10 +226,28 @@ half_to_float4 (const guint16 h[4], half_to_float4_c (h, f); } +void +float_to_half (const float *f, + guint16 *h, + int n) +{ + float_to_half_c (f, h, n); +} + +void +half_to_float (const guint16 *h, + float *f, + int n) +{ + half_to_float_c (h, f, n); +} + #else void float_to_half4 (const float f[4], guint16 h[4]) __attribute__((alias ("float_to_half4_c"))); void half_to_float4 (const guint16 h[4], float f[4]) __attribute__((alias ("half_to_float4_c"))); +void float_to_half (const float *f, guint16 *h, int n) __attribute__((alias ("float_to_half_c"))); +void half_to_float (const guint16 *h, float *f, int n) __attribute__((alias ("half_to_float_c"))); #endif diff --git a/gsk/ngl/fp16i.c b/gsk/ngl/fp16i.c index 74c5827ff8..aff38411bd 100644 --- a/gsk/ngl/fp16i.c +++ b/gsk/ngl/fp16i.c @@ -30,7 +30,6 @@ #else #define CAST_M128I_P(a) (__m128i_u const *) a #endif - void float_to_half4_f16c (const float f[4], guint16 h[4]) @@ -50,4 +49,74 @@ half_to_float4_f16c (const guint16 h[4], _mm_store_ps (f, s); } +#define ALIGNED(p, n) (GPOINTER_TO_UINT(p) % n == 0) +void +float_to_half_f16c (const float *f, + guint16 *h, + int n) +{ + __m128 s; + __m128i i; + int j; + const float *ff = f; + guint16 *hh = h; + + for (j = 0; j < n; j++) + { + if (ALIGNED (ff, 16) && ALIGNED (hh, 16)) + break; + ff++; + hh++; + } + + float_to_half_c (f, h, j); + + for (; j + 4 < n; j += 4) + { + s = _mm_loadu_ps (ff); + i = _mm_cvtps_ph (s, 0); + _mm_storel_epi64 ((__m128i*)hh, i); + ff += 4; + hh += 4; + } + + if (j < n) + float_to_half_c (ff, hh, n - j); +} + +void +half_to_float_f16c (const guint16 *h, + float *f, + int n) +{ + __m128i i; + __m128 s; + int j; + const guint16 *hh = h; + float *ff = f; + + for (j = 0; j < n; j++) + { + if (ALIGNED (ff, 16) && ALIGNED (hh, 16)) + break; + ff++; + hh++; + } + + half_to_float_c (h, f, j); + + for (; j + 4 < n; j += 4) + { + i = _mm_loadl_epi64 (CAST_M128I_P (hh)); + s = _mm_cvtph_ps (i); + _mm_store_ps (ff, s); + hh += 4; + ff += 4; + } + + if (j < n) + half_to_float_c (hh, ff, n - j); +} + #endif /* HAVE_F16C */ + diff --git a/gsk/ngl/fp16private.h b/gsk/ngl/fp16private.h index 574d7e4388..fbb95cdc1b 100644 --- a/gsk/ngl/fp16private.h +++ b/gsk/ngl/fp16private.h @@ -35,18 +35,42 @@ void float_to_half4 (const float f[4], void half_to_float4 (const guint16 h[4], float f[4]); +void float_to_half (const float *f, + guint16 *h, + int n); + +void half_to_float (const guint16 *h, + float *f, + int n); + void float_to_half4_f16c (const float f[4], guint16 h[4]); void half_to_float4_f16c (const guint16 h[4], float f[4]); +void float_to_half_f16c (const float *f, + guint16 *h, + int n); + +void half_to_float_f16c (const guint16 *h, + float *f, + int n); + void float_to_half4_c (const float f[4], guint16 h[4]); void half_to_float4_c (const guint16 h[4], float f[4]); +void float_to_half_c (const float *f, + guint16 *h, + int n); + +void half_to_float_c (const guint16 *h, + float *f, + int n); + G_END_DECLS #endif diff --git a/testsuite/gsk/half-float.c b/testsuite/gsk/half-float.c index 35dce9c1c8..ba56992f21 100644 --- a/testsuite/gsk/half-float.c +++ b/testsuite/gsk/half-float.c @@ -32,6 +32,23 @@ test_constants (void) } } +static float +random_representable_float (void) +{ + guint16 h[4]; + float f[4]; + do + { + /* generate a random float thats representable as fp16 */ + memset (h, 0, sizeof (h)); + h[0] = g_random_int_range (G_MININT16, G_MAXINT16); + half_to_float4 (h, f); + } + while (!isnormal (f[0])); /* skip nans and infs since they don't compare well */ + + return f[0]; +} + static void test_roundtrip (void) { @@ -41,15 +58,7 @@ test_roundtrip (void) float f2[4]; guint16 h[4]; - do - { - /* generate a random float thats representable as fp16 */ - memset (h, 0, sizeof (h)); - h[0] = g_random_int_range (G_MININT16, G_MAXINT16); - half_to_float4 (h, f2); - } - while (!isnormal (f2[0])); /* skip nans and infs since they don't compare well */ - + f2[0] = random_representable_float (); memset (f, 0, sizeof (f)); f[0] = f2[0]; @@ -60,6 +69,32 @@ test_roundtrip (void) } } +/* Test that the array version work as expected, + * in particular with unaligned boundaries. + */ +static void +test_many (void) +{ + for (int i = 0; i < 100; i++) + { + int size = g_random_int_range (100, 200); + int offset = g_random_int_range (0, 20); + + guint16 *h = g_new0 (guint16, size); + float *f = g_new0 (float, size); + float *f2 = g_new0 (float, size); + + for (int j = offset; j < size; j++) + f[j] = random_representable_float (); + + float_to_half (f + offset, h + offset, size - offset); + half_to_float (h + offset, f2 + offset, size - offset); + + for (int j = offset; j < size; j++) + g_assert_cmpfloat (f[j], ==, f2[j]); + } +} + int main (int argc, char *argv[]) { @@ -67,6 +102,7 @@ main (int argc, char *argv[]) g_test_add_func ("/half-float/constants", test_constants); g_test_add_func ("/half-float/roundtrip", test_roundtrip); + g_test_add_func ("/half-float/many", test_many); return g_test_run (); } From c1c1d4431ded3e6bf6354ff1d5dd8506fb90eb2d Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 8 Sep 2021 19:50:09 -0400 Subject: [PATCH 3/3] Refactor gdk_gl_context_upload_texture slightly Introduce a gl_internalformat variable. This will let us handle more formats in a uniform way in future commits. --- gdk/gdkglcontext.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/gdk/gdkglcontext.c b/gdk/gdkglcontext.c index 58dba198ab..522cbcecf6 100644 --- a/gdk/gdkglcontext.c +++ b/gdk/gdkglcontext.c @@ -229,6 +229,7 @@ gdk_gl_context_upload_texture (GdkGLContext *context, { GdkGLContextPrivate *priv = gdk_gl_context_get_instance_private (context); guchar *copy = NULL; + guint gl_internalformat; guint gl_format; guint gl_type; guint bpp; @@ -250,6 +251,7 @@ gdk_gl_context_upload_texture (GdkGLContext *context, } bpp = 4; + gl_internalformat = GL_RGBA8; gl_format = GL_RGBA; gl_type = GL_UNSIGNED_BYTE; } @@ -257,18 +259,21 @@ gdk_gl_context_upload_texture (GdkGLContext *context, { if (data_format == GDK_MEMORY_DEFAULT) /* Cairo surface format */ { + gl_internalformat = GL_RGBA8; gl_format = GL_BGRA; gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; bpp = 4; } else if (data_format == GDK_MEMORY_R8G8B8) /* Pixmap non-alpha data */ { + gl_internalformat = GL_RGBA8; gl_format = GL_RGB; gl_type = GL_UNSIGNED_BYTE; bpp = 3; } else if (data_format == GDK_MEMORY_B8G8R8) { + gl_internalformat = GL_RGBA8; gl_format = GL_BGR; gl_type = GL_UNSIGNED_BYTE; bpp = 3; @@ -283,6 +288,7 @@ gdk_gl_context_upload_texture (GdkGLContext *context, stride = width * 4; bpp = 4; data = copy; + gl_internalformat = GL_RGBA8; gl_format = GL_BGRA; gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; } @@ -295,7 +301,7 @@ gdk_gl_context_upload_texture (GdkGLContext *context, { glPixelStorei (GL_UNPACK_ALIGNMENT, 1); - glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data); + glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data); glPixelStorei (GL_UNPACK_ALIGNMENT, 4); } else if ((!priv->use_es || @@ -303,14 +309,14 @@ gdk_gl_context_upload_texture (GdkGLContext *context, { glPixelStorei (GL_UNPACK_ROW_LENGTH, stride / bpp); - glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, data); + glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, data); glPixelStorei (GL_UNPACK_ROW_LENGTH, 0); } else { int i; - glTexImage2D (texture_target, 0, GL_RGBA, width, height, 0, gl_format, gl_type, NULL); + glTexImage2D (texture_target, 0, gl_internalformat, width, height, 0, gl_format, gl_type, NULL); for (i = 0; i < height; i++) glTexSubImage2D (texture_target, 0, 0, i, width, 1, gl_format, gl_type, data + (i * stride)); }