2 Adds new NEON configure test and -no-neon configure option. NEON
3 implementations can also be turned off by setting the QT_NO_NEON
6 Performance improvements (in frames per second):
7 - Blending ARGB32 on RGB32/ARGB32, mostly opaque: 71 %
8 - Blending ARGB32 on RGB32/ARGB32, no opaque pixels: 108 %
9 - Blending ARGB32 on RGB32/ARGB32, with 0.5 opacity: 158 %
10 - Blending RGB32 on RGB32/ARGB32, with 0.5 opacity: 189 %
12 Task-number: QTBUG-6684
13 Reviewed-by: Gunnar Sletta
14 Reviewed-by: Paul Olav Tvete
16 config.tests/unix/neon/neon.cpp | 51 +++++++
17 config.tests/unix/neon/neon.pro | 2 +
19 src/gui/painting/painting.pri | 7 +
20 src/gui/painting/qblendfunctions.cpp | 2 +-
21 src/gui/painting/qdrawhelper.cpp | 16 ++-
22 src/gui/painting/qdrawhelper_neon.cpp | 260 +++++++++++++++++++++++++++++++++
23 src/gui/painting/qdrawhelper_neon_p.h | 76 ++++++++++
24 8 files changed, 432 insertions(+), 4 deletions(-)
25 create mode 100644 config.tests/unix/neon/neon.cpp
26 create mode 100644 config.tests/unix/neon/neon.pro
27 create mode 100644 src/gui/painting/qdrawhelper_neon.cpp
28 create mode 100644 src/gui/painting/qdrawhelper_neon_p.h
30 diff --git a/config.tests/unix/neon/neon.cpp b/config.tests/unix/neon/neon.cpp
32 index 0000000..c31a9fd
34 +++ b/config.tests/unix/neon/neon.cpp
36 +/****************************************************************************
38 +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
39 +** All rights reserved.
40 +** Contact: Nokia Corporation (qt-info@nokia.com)
42 +** This file is part of the config.tests of the Qt Toolkit.
44 +** $QT_BEGIN_LICENSE:LGPL$
45 +** No Commercial Usage
46 +** This file contains pre-release code and may not be distributed.
47 +** You may use this file in accordance with the terms and conditions
48 +** contained in the Technology Preview License Agreement accompanying
51 +** GNU Lesser General Public License Usage
52 +** Alternatively, this file may be used under the terms of the GNU Lesser
53 +** General Public License version 2.1 as published by the Free Software
54 +** Foundation and appearing in the file LICENSE.LGPL included in the
55 +** packaging of this file. Please review the following information to
56 +** ensure the GNU Lesser General Public License version 2.1 requirements
57 +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
59 +** In addition, as a special exception, Nokia gives you certain additional
60 +** rights. These rights are described in the Nokia Qt LGPL Exception
61 +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
63 +** If you have questions regarding the use of this file, please contact
64 +** Nokia at qt-info@nokia.com.
75 +****************************************************************************/
77 +#include <arm_neon.h>
79 +int main(int, char**)
81 + int32x4_t null = vdupq_n_s32(0x0);
84 + vst1q_lane_s32(&result, null, 0);
87 diff --git a/config.tests/unix/neon/neon.pro b/config.tests/unix/neon/neon.pro
89 index 0000000..de20c4e
91 +++ b/config.tests/unix/neon/neon.pro
95 diff --git a/configure b/configure
96 index 2114863..22e6bd4 100755
99 @@ -745,6 +745,7 @@ CFG_HOST_ENDIAN=auto
100 CFG_DOUBLEFORMAT=auto
104 CFG_CLOCK_GETTIME=auto
105 CFG_CLOCK_MONOTONIC=auto
107 @@ -1604,6 +1605,13 @@ while [ "$#" -gt 0 ]; do
112 + if [ "$VAL" = "no" ]; then
119 if [ "$VAL" = "yes" ] || [ "$VAL" = "no" ]; then
120 CFG_REDUCE_RELOCATIONS="$VAL"
121 @@ -3889,6 +3897,7 @@ Qt for Embedded Linux only:
122 -iwmmxt ............ Compile using the iWMMXt instruction set
123 (available on some XScale CPUs).
125 + -no-neon ........... Do not compile with use of NEON instructions.
129 @@ -4509,6 +4518,15 @@ if [ "$CFG_IWMMXT" = "yes" ]; then
133 +# detect neon support
134 +if ([ "${CFG_ARCH}" = "arm" ] || [ "${CFG_ARCH}" = "armv6" ]) && [ "${CFG_NEON}" = "auto" ]; then
135 + if "$unixtests/compile.test" "$XQMAKESPEC" "$QMAKE_CONFIG" $OPT_VERBOSE "$relpath" "$outpath" config.tests/unix/neon "neon" $L_FLAGS $I_FLAGS $l_FLAGS "-mfpu=neon"; then
143 if [ "$CFG_ZLIB" = "no" ]; then
144 # Note: Qt no longer support builds without zlib
145 @@ -6124,6 +6142,7 @@ fi
146 [ "$CFG_SSE" = "yes" ] && QMAKE_CONFIG="$QMAKE_CONFIG sse"
147 [ "$CFG_SSE2" = "yes" ] && QMAKE_CONFIG="$QMAKE_CONFIG sse2"
148 [ "$CFG_IWMMXT" = "yes" ] && QMAKE_CONFIG="$QMAKE_CONFIG iwmmxt"
149 +[ "$CFG_NEON" = "yes" ] && QMAKE_CONFIG="$QMAKE_CONFIG neon"
150 [ "$PLATFORM_MAC" = "yes" ] && QMAKE_CONFIG="$QMAKE_CONFIG $CFG_MAC_ARCHS"
151 if [ "$CFG_IPV6" = "yes" ]; then
152 QT_CONFIG="$QT_CONFIG ipv6"
153 @@ -7416,8 +7435,9 @@ echo "Declarative module .. $CFG_DECLARATIVE"
154 echo "STL support ......... $CFG_STL"
155 echo "PCH support ......... $CFG_PRECOMPILE"
156 echo "MMX/3DNOW/SSE/SSE2.. ${CFG_MMX}/${CFG_3DNOW}/${CFG_SSE}/${CFG_SSE2}"
157 -if [ "${CFG_ARCH}" = "arm" ]; then
158 +if [ "${CFG_ARCH}" = "arm" ] || [ "${CFG_ARCH}" = "armv6" ]; then
159 echo "iWMMXt support ...... ${CFG_IWMMXT}"
160 + echo "NEON support ........ ${CFG_NEON}"
162 [ "${PLATFORM_QWS}" != "yes" ] && echo "Graphics System ..... $CFG_GRAPHICS_SYSTEM"
163 echo "IPv6 support ........ $CFG_IPV6"
164 diff --git a/src/gui/painting/painting.pri b/src/gui/painting/painting.pri
165 index 628a109..0b1e79a 100644
166 --- a/src/gui/painting/painting.pri
167 +++ b/src/gui/painting/painting.pri
168 @@ -379,6 +379,13 @@ symbian {
169 QMAKE_CXXFLAGS.ARMCC *= -O3
173 + DEFINES += QT_HAVE_NEON
174 + HEADERS += painting/qdrawhelper_neon_p.h
175 + SOURCES += painting/qdrawhelper_neon.cpp
179 contains(QT_CONFIG, zlib) {
180 INCLUDEPATH += ../3rdparty/zlib
181 } else:!contains(QT_CONFIG, no-zlib) {
182 diff --git a/src/gui/painting/qblendfunctions.cpp b/src/gui/painting/qblendfunctions.cpp
183 index 1d15dac..81d1515 100644
184 --- a/src/gui/painting/qblendfunctions.cpp
185 +++ b/src/gui/painting/qblendfunctions.cpp
186 @@ -605,7 +605,7 @@ static void qt_blend_argb32_on_argb32(uchar *destPixels, int dbpl,
190 -static void qt_blend_rgb32_on_rgb32(uchar *destPixels, int dbpl,
191 +void qt_blend_rgb32_on_rgb32(uchar *destPixels, int dbpl,
192 const uchar *srcPixels, int sbpl,
195 diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp
196 index 23236ec..84cf5cc 100644
197 --- a/src/gui/painting/qdrawhelper.cpp
198 +++ b/src/gui/painting/qdrawhelper.cpp
200 #include <private/qpainter_p.h>
201 #include <private/qdrawhelper_x86_p.h>
202 #include <private/qdrawhelper_armv6_p.h>
203 +#include <private/qdrawhelper_neon_p.h>
204 #include <private/qmath_p.h>
207 @@ -7725,7 +7726,8 @@ enum CPUFeatures {
216 static uint detectCPUFeatures()
217 @@ -7751,6 +7753,9 @@ static uint detectCPUFeatures()
218 // runtime detection only available when running as a previlegied process
219 static const bool doIWMMXT = !qgetenv("QT_NO_IWMMXT").toInt();
220 return doIWMMXT ? IWMMXT : 0;
221 +#elif defined(QT_HAVE_NEON)
222 + static const bool doNEON = !qgetenv("QT_NO_NEON").toInt();
223 + return doNEON ? NEON : 0;
226 #if defined(__x86_64__) || defined(Q_OS_WIN64)
227 @@ -8122,7 +8127,14 @@ void qInitDrawhelperAsm()
228 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_armv6;
229 qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_armv6;
230 qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_armv6;
231 -#endif // Q_CC_RVCT && QT_HAVE_ARMV6
232 +#elif defined(QT_HAVE_NEON)
233 + if (features & NEON) {
234 + qBlendFunctions[QImage::Format_RGB32][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon;
235 + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_RGB32] = qt_blend_rgb32_on_rgb32_neon;
236 + qBlendFunctions[QImage::Format_RGB32][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon;
237 + qBlendFunctions[QImage::Format_ARGB32_Premultiplied][QImage::Format_ARGB32_Premultiplied] = qt_blend_argb32_on_argb32_neon;
241 if (functionForModeSolidAsm) {
242 const int destinationMode = QPainter::CompositionMode_Destination;
243 diff --git a/src/gui/painting/qdrawhelper_neon.cpp b/src/gui/painting/qdrawhelper_neon.cpp
245 index 0000000..7fe11bf
247 +++ b/src/gui/painting/qdrawhelper_neon.cpp
249 +/****************************************************************************
251 +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
252 +** All rights reserved.
253 +** Contact: Nokia Corporation (qt-info@nokia.com)
255 +** This file is part of the QtGui module of the Qt Toolkit.
257 +** $QT_BEGIN_LICENSE:LGPL$
258 +** No Commercial Usage
259 +** This file contains pre-release code and may not be distributed.
260 +** You may use this file in accordance with the terms and conditions
261 +** contained in the Technology Preview License Agreement accompanying
264 +** GNU Lesser General Public License Usage
265 +** Alternatively, this file may be used under the terms of the GNU Lesser
266 +** General Public License version 2.1 as published by the Free Software
267 +** Foundation and appearing in the file LICENSE.LGPL included in the
268 +** packaging of this file. Please review the following information to
269 +** ensure the GNU Lesser General Public License version 2.1 requirements
270 +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
272 +** In addition, as a special exception, Nokia gives you certain additional
273 +** rights. These rights are described in the Nokia Qt LGPL Exception
274 +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
276 +** If you have questions regarding the use of this file, please contact
277 +** Nokia at qt-info@nokia.com.
288 +****************************************************************************/
290 +#include <private/qdrawhelper_p.h>
294 +#include <private/qdrawhelper_neon_p.h>
295 +#include <arm_neon.h>
299 +static inline int16x8_t qvdiv_255_s16(int16x8_t x, int16x8_t half)
301 + // result = (x + (x >> 8) + 0x80) >> 8
303 + const int16x8_t temp = vshrq_n_s16(x, 8); // x >> 8
304 + const int16x8_t sum_part = vaddq_s16(x, half); // x + 0x80
305 + const int16x8_t sum = vaddq_s16(temp, sum_part);
307 + return vreinterpretq_s16_u16(vshrq_n_u16(vreinterpretq_u16_s16(sum), 8));
310 +static inline int16x8_t qvbyte_mul_s16(int16x8_t x, int16x8_t alpha, int16x8_t half)
312 + // t = qRound(x * alpha / 255.0)
314 + const int16x8_t t = vmulq_s16(x, alpha); // t
315 + return qvdiv_255_s16(t, half);
318 +static inline int16x8_t qvinterpolate_pixel_255(int16x8_t x, int16x8_t a, int16x8_t y, int16x8_t b, int16x8_t half)
320 + // t = x * a + y * b
322 + const int16x8_t ta = vmulq_s16(x, a);
323 + const int16x8_t tb = vmulq_s16(y, b);
325 + return qvdiv_255_s16(vaddq_s16(ta, tb), half);
328 +static inline int16x8_t qvsource_over_s16(int16x8_t src16, int16x8_t dst16, int16x8_t half, int16x8_t full)
330 + const int16x4_t alpha16_high = vdup_lane_s16(vget_high_s16(src16), 3);
331 + const int16x4_t alpha16_low = vdup_lane_s16(vget_low_s16(src16), 3);
333 + const int16x8_t alpha16 = vsubq_s16(full, vcombine_s16(alpha16_low, alpha16_high));
335 + return vaddq_s16(src16, qvbyte_mul_s16(dst16, alpha16, half));
338 +void qt_blend_argb32_on_argb32_neon(uchar *destPixels, int dbpl,
339 + const uchar *srcPixels, int sbpl,
343 + const uint *src = (const uint *) srcPixels;
344 + uint *dst = (uint *) destPixels;
345 + int16x8_t half = vdupq_n_s16(0x80);
346 + int16x8_t full = vdupq_n_s16(0xff);
347 + if (const_alpha == 256) {
348 + for (int y = 0; y < h; ++y) {
350 + for (; x < w-3; x += 4) {
351 + int32x4_t src32 = vld1q_s32((int32_t *)&src[x]);
352 + if ((src[x] & src[x+1] & src[x+2] & src[x+3]) >= 0xff000000) {
354 + vst1q_s32((int32_t *)&dst[x], src32);
355 + } else if (src[x] | src[x+1] | src[x+2] | src[x+3]) {
356 + int32x4_t dst32 = vld1q_s32((int32_t *)&dst[x]);
358 + const uint8x16_t src8 = vreinterpretq_u8_s32(src32);
359 + const uint8x16_t dst8 = vreinterpretq_u8_s32(dst32);
361 + const uint8x8_t src8_low = vget_low_u8(src8);
362 + const uint8x8_t dst8_low = vget_low_u8(dst8);
364 + const uint8x8_t src8_high = vget_high_u8(src8);
365 + const uint8x8_t dst8_high = vget_high_u8(dst8);
367 + const int16x8_t src16_low = vreinterpretq_s16_u16(vmovl_u8(src8_low));
368 + const int16x8_t dst16_low = vreinterpretq_s16_u16(vmovl_u8(dst8_low));
370 + const int16x8_t src16_high = vreinterpretq_s16_u16(vmovl_u8(src8_high));
371 + const int16x8_t dst16_high = vreinterpretq_s16_u16(vmovl_u8(dst8_high));
373 + const int16x8_t result16_low = qvsource_over_s16(src16_low, dst16_low, half, full);
374 + const int16x8_t result16_high = qvsource_over_s16(src16_high, dst16_high, half, full);
376 + const int32x2_t result32_low = vreinterpret_s32_s8(vmovn_s16(result16_low));
377 + const int32x2_t result32_high = vreinterpret_s32_s8(vmovn_s16(result16_high));
379 + vst1q_s32((int32_t *)&dst[x], vcombine_s32(result32_low, result32_high));
384 + if (s >= 0xff000000)
387 + dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s));
389 + dst = (quint32 *)(((uchar *) dst) + dbpl);
390 + src = (const quint32 *)(((const uchar *) src) + sbpl);
392 + } else if (const_alpha != 0) {
393 + const_alpha = (const_alpha * 255) >> 8;
394 + int16x8_t const_alpha16 = vdupq_n_s16(const_alpha);
395 + for (int y = 0; y < h; ++y) {
397 + for (; x < w-3; x += 4) {
398 + if (src[x] | src[x+1] | src[x+2] | src[x+3]) {
399 + int32x4_t src32 = vld1q_s32((int32_t *)&src[x]);
400 + int32x4_t dst32 = vld1q_s32((int32_t *)&dst[x]);
402 + const uint8x16_t src8 = vreinterpretq_u8_s32(src32);
403 + const uint8x16_t dst8 = vreinterpretq_u8_s32(dst32);
405 + const uint8x8_t src8_low = vget_low_u8(src8);
406 + const uint8x8_t dst8_low = vget_low_u8(dst8);
408 + const uint8x8_t src8_high = vget_high_u8(src8);
409 + const uint8x8_t dst8_high = vget_high_u8(dst8);
411 + const int16x8_t src16_low = vreinterpretq_s16_u16(vmovl_u8(src8_low));
412 + const int16x8_t dst16_low = vreinterpretq_s16_u16(vmovl_u8(dst8_low));
414 + const int16x8_t src16_high = vreinterpretq_s16_u16(vmovl_u8(src8_high));
415 + const int16x8_t dst16_high = vreinterpretq_s16_u16(vmovl_u8(dst8_high));
417 + const int16x8_t srcalpha16_low = qvbyte_mul_s16(src16_low, const_alpha16, half);
418 + const int16x8_t srcalpha16_high = qvbyte_mul_s16(src16_high, const_alpha16, half);
420 + const int16x8_t result16_low = qvsource_over_s16(srcalpha16_low, dst16_low, half, full);
421 + const int16x8_t result16_high = qvsource_over_s16(srcalpha16_high, dst16_high, half, full);
423 + const int32x2_t result32_low = vreinterpret_s32_s8(vmovn_s16(result16_low));
424 + const int32x2_t result32_high = vreinterpret_s32_s8(vmovn_s16(result16_high));
426 + vst1q_s32((int32_t *)&dst[x], vcombine_s32(result32_low, result32_high));
432 + s = BYTE_MUL(s, const_alpha);
433 + dst[x] = s + BYTE_MUL(dst[x], qAlpha(~s));
436 + dst = (quint32 *)(((uchar *) dst) + dbpl);
437 + src = (const quint32 *)(((const uchar *) src) + sbpl);
442 +// qblendfunctions.cpp
443 +void qt_blend_rgb32_on_rgb32(uchar *destPixels, int dbpl,
444 + const uchar *srcPixels, int sbpl,
448 +void qt_blend_rgb32_on_rgb32_neon(uchar *destPixels, int dbpl,
449 + const uchar *srcPixels, int sbpl,
453 + if (const_alpha != 256) {
454 + if (const_alpha != 0) {
455 + const uint *src = (const uint *) srcPixels;
456 + uint *dst = (uint *) destPixels;
457 + int16x8_t half = vdupq_n_s16(0x80);
458 + const_alpha = (const_alpha * 255) >> 8;
459 + int one_minus_const_alpha = 255 - const_alpha;
460 + int16x8_t const_alpha16 = vdupq_n_s16(const_alpha);
461 + int16x8_t one_minus_const_alpha16 = vdupq_n_s16(255 - const_alpha);
462 + for (int y = 0; y < h; ++y) {
464 + for (; x < w-3; x += 4) {
465 + int32x4_t src32 = vld1q_s32((int32_t *)&src[x]);
466 + int32x4_t dst32 = vld1q_s32((int32_t *)&dst[x]);
468 + const uint8x16_t src8 = vreinterpretq_u8_s32(src32);
469 + const uint8x16_t dst8 = vreinterpretq_u8_s32(dst32);
471 + const uint8x8_t src8_low = vget_low_u8(src8);
472 + const uint8x8_t dst8_low = vget_low_u8(dst8);
474 + const uint8x8_t src8_high = vget_high_u8(src8);
475 + const uint8x8_t dst8_high = vget_high_u8(dst8);
477 + const int16x8_t src16_low = vreinterpretq_s16_u16(vmovl_u8(src8_low));
478 + const int16x8_t dst16_low = vreinterpretq_s16_u16(vmovl_u8(dst8_low));
480 + const int16x8_t src16_high = vreinterpretq_s16_u16(vmovl_u8(src8_high));
481 + const int16x8_t dst16_high = vreinterpretq_s16_u16(vmovl_u8(dst8_high));
483 + const int16x8_t result16_low = qvinterpolate_pixel_255(src16_low, const_alpha16, dst16_low, one_minus_const_alpha16, half);
484 + const int16x8_t result16_high = qvinterpolate_pixel_255(src16_high, const_alpha16, dst16_high, one_minus_const_alpha16, half);
486 + const int32x2_t result32_low = vreinterpret_s32_s8(vmovn_s16(result16_low));
487 + const int32x2_t result32_high = vreinterpret_s32_s8(vmovn_s16(result16_high));
489 + vst1q_s32((int32_t *)&dst[x], vcombine_s32(result32_low, result32_high));
493 + s = BYTE_MUL(s, const_alpha);
494 + dst[x] = INTERPOLATE_PIXEL_255(src[x], const_alpha, dst[x], one_minus_const_alpha);
496 + dst = (quint32 *)(((uchar *) dst) + dbpl);
497 + src = (const quint32 *)(((const uchar *) src) + sbpl);
501 + qt_blend_rgb32_on_rgb32(destPixels, dbpl, srcPixels, sbpl, w, h, const_alpha);
507 +#endif // QT_HAVE_NEON
509 diff --git a/src/gui/painting/qdrawhelper_neon_p.h b/src/gui/painting/qdrawhelper_neon_p.h
511 index 0000000..cb9a0d6
513 +++ b/src/gui/painting/qdrawhelper_neon_p.h
515 +/****************************************************************************
517 +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
518 +** All rights reserved.
519 +** Contact: Nokia Corporation (qt-info@nokia.com)
521 +** This file is part of the QtGui module of the Qt Toolkit.
523 +** $QT_BEGIN_LICENSE:LGPL$
524 +** No Commercial Usage
525 +** This file contains pre-release code and may not be distributed.
526 +** You may use this file in accordance with the terms and conditions
527 +** contained in the Technology Preview License Agreement accompanying
530 +** GNU Lesser General Public License Usage
531 +** Alternatively, this file may be used under the terms of the GNU Lesser
532 +** General Public License version 2.1 as published by the Free Software
533 +** Foundation and appearing in the file LICENSE.LGPL included in the
534 +** packaging of this file. Please review the following information to
535 +** ensure the GNU Lesser General Public License version 2.1 requirements
536 +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
538 +** In addition, as a special exception, Nokia gives you certain additional
539 +** rights. These rights are described in the Nokia Qt LGPL Exception
540 +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
542 +** If you have questions regarding the use of this file, please contact
543 +** Nokia at qt-info@nokia.com.
554 +****************************************************************************/
556 +#ifndef QDRAWHELPER_NEON_P_H
557 +#define QDRAWHELPER_NEON_P_H
563 +// This file is not part of the Qt API. It exists purely as an
564 +// implementation detail. This header file may change from version to
565 +// version without notice, or even be removed.
570 +#include <private/qdrawhelper_p.h>
576 +void qt_blend_argb32_on_argb32_neon(uchar *destPixels, int dbpl,
577 + const uchar *srcPixels, int sbpl,
581 +void qt_blend_rgb32_on_rgb32_neon(uchar *destPixels, int dbpl,
582 + const uchar *srcPixels, int sbpl,
586 +#endif // QT_HAVE_NEON
590 +#endif // QDRAWHELPER_NEON_P_H