diff options
Diffstat (limited to 'src/gui')
24 files changed, 1434 insertions, 837 deletions
diff --git a/src/gui/doc/snippets/code/src_gui_image_qpixmapcache.cpp b/src/gui/doc/snippets/code/src_gui_image_qpixmapcache.cpp index 3870237ac3..9043ee6361 100644 --- a/src/gui/doc/snippets/code/src_gui_image_qpixmapcache.cpp +++ b/src/gui/doc/snippets/code/src_gui_image_qpixmapcache.cpp @@ -49,9 +49,8 @@ ****************************************************************************/ //! [0] -QPixmap* pp; QPixmap p; -if ((pp=QPixmapCache::find("my_big_image", pm))) { +if (QPixmap *pp = QPixmapCache::find("my_big_image"))) { p = *pp; } else { p.load("bigimage.png"); diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index ce0c6170c1..189f12fd5c 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -68,6 +68,11 @@ #include <private/qimage_p.h> #include <private/qfont_p.h> +#if QT_CONFIG(thread) +#include "qsemaphore.h" +#include "qthreadpool.h" +#endif + QT_BEGIN_NAMESPACE static inline bool isLocked(QImageData *data) @@ -4861,18 +4866,43 @@ void QImage::applyColorTransform(const QColorTransform &transform) Q_UNREACHABLE(); } + std::function<void(int,int)> transformSegment; + if (depth() > 32) { - for (int i = 0; i < height(); ++i) { - QRgba64 *scanline = reinterpret_cast<QRgba64 *>(scanLine(i)); - transform.d->apply(scanline, scanline, width(), flags); - } + transformSegment = [&](int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + QRgba64 *scanline = reinterpret_cast<QRgba64 *>(scanLine(y)); + transform.d->apply(scanline, scanline, width(), flags); + } + }; } else { - for (int i = 0; i < height(); ++i) { - QRgb *scanline = reinterpret_cast<QRgb *>(scanLine(i)); - transform.d->apply(scanline, scanline, width(), flags); - } + transformSegment = [&](int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + QRgb *scanline = reinterpret_cast<QRgb *>(scanLine(y)); + transform.d->apply(scanline, scanline, width(), flags); + } + }; } +#if QT_CONFIG(thread) + int segments = sizeInBytes() / (1<<16); + segments = std::min(segments, height()); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (height() - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + transformSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + } else +#endif + transformSegment(0, height()); + if (oldFormat != format()) *this = std::move(*this).convertToFormat(oldFormat); } diff --git a/src/gui/image/qimage_conversions.cpp b/src/gui/image/qimage_conversions.cpp index 27088698ec..4f570d8684 100644 --- a/src/gui/image/qimage_conversions.cpp +++ b/src/gui/image/qimage_conversions.cpp @@ -43,7 +43,12 @@ #include <private/qendian_p.h> #include <private/qsimd_p.h> #include <private/qimage_p.h> + #include <qendian.h> +#if QT_CONFIG(thread) +#include <qsemaphore.h> +#include <qthreadpool.h> +#endif QT_BEGIN_NAMESPACE @@ -159,12 +164,8 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio // Cannot be used with indexed formats. Q_ASSERT(dest->format > QImage::Format_Indexed8); Q_ASSERT(src->format > QImage::Format_Indexed8); - uint buf[BufferSize]; - uint *buffer = buf; const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; const QPixelLayout *destLayout = &qPixelLayouts[dest->format]; - const uchar *srcData = src->data; - uchar *destData = dest->data; FetchAndConvertPixelsFunc fetch = srcLayout->fetchToARGB32PM; ConvertAndStorePixelsFunc store = destLayout->storeFromARGB32PM; @@ -197,59 +198,110 @@ void convert_generic(QImageData *dest, const QImageData *src, Qt::ImageConversio else store = destLayout->storeFromRGB32; } - QDitherInfo dither; - QDitherInfo *ditherPtr = nullptr; - if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) - ditherPtr = &dither; - - for (int y = 0; y < src->height; ++y) { - dither.y = y; - int x = 0; - while (x < src->width) { - dither.x = x; - int l = src->width - x; - if (destLayout->bpp == QPixelLayout::BPP32) - buffer = reinterpret_cast<uint *>(destData) + x; - else - l = qMin(l, BufferSize); - const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr); - store(destData, ptr, x, l, nullptr, ditherPtr); - x += l; + + auto convertSegment = [=](int yStart, int yEnd) { + uint buf[BufferSize]; + uint *buffer = buf; + const uchar *srcData = src->data + src->bytes_per_line * yStart; + uchar *destData = dest->data + dest->bytes_per_line * yStart; + QDitherInfo dither; + QDitherInfo *ditherPtr = nullptr; + if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) + ditherPtr = &dither; + for (int y = yStart; y < yEnd; ++y) { + dither.y = y; + int x = 0; + while (x < src->width) { + dither.x = x; + int l = src->width - x; + if (destLayout->bpp == QPixelLayout::BPP32) + buffer = reinterpret_cast<uint *>(destData) + x; + else + l = qMin(l, BufferSize); + const uint *ptr = fetch(buffer, srcData, x, l, 0, ditherPtr); + store(destData, ptr, x, l, 0, ditherPtr); + x += l; + } + srcData += src->bytes_per_line; + destData += dest->bytes_per_line; } - srcData += src->bytes_per_line; - destData += dest->bytes_per_line; - } + }; + +#if QT_CONFIG(thread) + int segments = src->nbytes / (1<<16); + segments = std::min(segments, src->height); + + if (segments <= 1) + return convertSegment(0, src->height); + + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (src->height - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + convertSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); +#else + convertSegment(0, src->height); +#endif } void convert_generic_to_rgb64(QImageData *dest, const QImageData *src, Qt::ImageConversionFlags) { Q_ASSERT(dest->format > QImage::Format_Indexed8); Q_ASSERT(src->format > QImage::Format_Indexed8); - QRgba64 buf[BufferSize]; - QRgba64 *buffer = buf; const QPixelLayout *srcLayout = &qPixelLayouts[src->format]; const QPixelLayout *destLayout = &qPixelLayouts[dest->format]; - const uchar *srcData = src->data; - uchar *destData = dest->data; const FetchAndConvertPixelsFunc64 fetch = srcLayout->fetchToRGBA64PM; const ConvertAndStorePixelsFunc64 store = qStoreFromRGBA64PM[dest->format]; - for (int y = 0; y < src->height; ++y) { - int x = 0; - while (x < src->width) { - int l = src->width - x; - if (destLayout->bpp == QPixelLayout::BPP64) - buffer = reinterpret_cast<QRgba64 *>(destData) + x; - else - l = qMin(l, BufferSize); - const QRgba64 *ptr = fetch(buffer, srcData, x, l, nullptr, nullptr); - store(destData, ptr, x, l, nullptr, nullptr); - x += l; + auto convertSegment = [=](int yStart, int yEnd) { + QRgba64 buf[BufferSize]; + QRgba64 *buffer = buf; + const uchar *srcData = src->data + yStart * src->bytes_per_line; + uchar *destData = dest->data + yStart * dest->bytes_per_line; + for (int y = yStart; y < yEnd; ++y) { + int x = 0; + while (x < src->width) { + int l = src->width - x; + if (destLayout->bpp == QPixelLayout::BPP64) + buffer = reinterpret_cast<QRgba64 *>(destData) + x; + else + l = qMin(l, BufferSize); + const QRgba64 *ptr = fetch(buffer, srcData, x, l, nullptr, nullptr); + store(destData, ptr, x, l, nullptr, nullptr); + x += l; + } + srcData += src->bytes_per_line; + destData += dest->bytes_per_line; } - srcData += src->bytes_per_line; - destData += dest->bytes_per_line; - } + }; +#if QT_CONFIG(thread) + int segments = src->nbytes / (1<<16); + segments = std::min(segments, src->height); + + if (segments <= 1) + return convertSegment(0, src->height); + + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (src->height - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + convertSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); +#else + convertSegment(0, src->height); +#endif } bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::ImageConversionFlags flags) @@ -270,11 +322,6 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im && qt_highColorPrecision(dst_format, !srcLayout->hasAlphaChannel)) return false; - uint buf[BufferSize]; - uint *buffer = buf; - uchar *srcData = data->data; - uchar *destData = data->data; - QImageData::ImageSizeParameters params = { data->bytes_per_line, data->nbytes }; if (data->depth != destDepth) { params = QImageData::calculateImageParameters(data->width, data->height, destDepth); @@ -313,28 +360,52 @@ bool convert_generic_inplace(QImageData *data, QImage::Format dst_format, Qt::Im else store = destLayout->storeFromRGB32; } - QDitherInfo dither; - QDitherInfo *ditherPtr = nullptr; - if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) - ditherPtr = &dither; - - for (int y = 0; y < data->height; ++y) { - dither.y = y; - int x = 0; - while (x < data->width) { - dither.x = x; - int l = data->width - x; - if (srcLayout->bpp == QPixelLayout::BPP32) - buffer = reinterpret_cast<uint *>(srcData) + x; - else - l = qMin(l, BufferSize); - const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr); - store(destData, ptr, x, l, nullptr, ditherPtr); - x += l; + + auto convertSegment = [=](int yStart, int yEnd) { + uint buf[BufferSize]; + uint *buffer = buf; + uchar *srcData = data->data + data->bytes_per_line * yStart; + uchar *destData = srcData; + QDitherInfo dither; + QDitherInfo *ditherPtr = nullptr; + if ((flags & Qt::PreferDither) && (flags & Qt::Dither_Mask) != Qt::ThresholdDither) + ditherPtr = &dither; + for (int y = yStart; y < yEnd; ++y) { + dither.y = y; + int x = 0; + while (x < data->width) { + dither.x = x; + int l = data->width - x; + if (srcLayout->bpp == QPixelLayout::BPP32) + buffer = reinterpret_cast<uint *>(srcData) + x; + else + l = qMin(l, BufferSize); + const uint *ptr = fetch(buffer, srcData, x, l, nullptr, ditherPtr); + store(destData, ptr, x, l, nullptr, ditherPtr); + x += l; + } + srcData += data->bytes_per_line; + destData += params.bytesPerLine; } - srcData += data->bytes_per_line; - destData += params.bytesPerLine; - } + }; +#if QT_CONFIG(thread) + int segments = data->nbytes / (1<<16); + segments = std::min(segments, data->height); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (data->height - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + convertSegment(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + } else +#endif + convertSegment(0, data->height); if (params.totalSize != data->nbytes) { Q_ASSERT(params.totalSize < data->nbytes); void *newData = realloc(data->data, params.totalSize); diff --git a/src/gui/painting/qimagescale.cpp b/src/gui/painting/qimagescale.cpp index 2e2f65b483..ecb0230e71 100644 --- a/src/gui/painting/qimagescale.cpp +++ b/src/gui/painting/qimagescale.cpp @@ -43,6 +43,11 @@ #include "qcolor.h" #include "qrgba64_p.h" +#if QT_CONFIG(thread) +#include "qsemaphore.h" +#include "qthreadpool.h" +#endif + QT_BEGIN_NAMESPACE /* @@ -239,6 +244,8 @@ static QImageScaleInfo* QImageScale::qimageCalcScaleInfo(const QImage &img, isi = new QImageScaleInfo; if (!isi) return nullptr; + isi->sh = sh; + isi->sw = sw; isi->xup_yup = (qAbs(dw) >= sw) + ((qAbs(dh) >= sh) << 1); @@ -303,33 +310,54 @@ static void qt_qimageScaleAARGBA_up_xy(QImageScaleInfo *isi, unsigned int *dest, int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - /* calculate the source line we'll scan from */ - const unsigned int *sptr = ypoints[y]; - unsigned int *dptr = dest + (y * dow); - const int yap = yapoints[y]; - if (yap > 0) { - for (int x = 0; x < dw; x++) { - const unsigned int *pix = sptr + xpoints[x]; - const int xap = xapoints[x]; - if (xap > 0) - *dptr = interpolate_4_pixels(pix, pix + sow, xap, yap); - else - *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - yap, pix[sow], yap); - dptr++; - } - } else { - for (int x = 0; x < dw; x++) { - const unsigned int *pix = sptr + xpoints[x]; - const int xap = xapoints[x]; - if (xap > 0) - *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - xap, pix[1], xap); - else - *dptr = pix[0]; - dptr++; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + /* calculate the source line we'll scan from */ + const unsigned int *sptr = ypoints[y]; + unsigned int *dptr = dest + (y * dow); + const int yap = yapoints[y]; + if (yap > 0) { + for (int x = 0; x < dw; x++) { + const unsigned int *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = interpolate_4_pixels(pix, pix + sow, xap, yap); + else + *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - yap, pix[sow], yap); + dptr++; + } + } else { + for (int x = 0; x < dw; x++) { + const unsigned int *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = INTERPOLATE_PIXEL_256(pix[0], 256 - xap, pix[1], xap); + else + *dptr = pix[0]; + dptr++; + } } } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } /* scale by area sampling - with alpha */ @@ -411,33 +439,54 @@ static void qt_qimageScaleAARGBA_up_x_down_y(QImageScaleInfo *isi, unsigned int int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int r, g, b, a; - qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow, r, g, b, a); - - int xap = xapoints[x]; - if (xap > 0) { - int rr, gg, bb, aa; - qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa); - - r = r * (256 - xap); - g = g * (256 - xap); - b = b * (256 - xap); - a = a * (256 - xap); - r = (r + (rr * xap)) >> 8; - g = (g + (gg * xap)) >> 8; - b = (b + (bb * xap)) >> 8; - a = (a + (aa * xap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; + + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int r, g, b, a; + qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow, r, g, b, a); + + int xap = xapoints[x]; + if (xap > 0) { + int rr, gg, bb, aa; + qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa); + + r = r * (256 - xap); + g = g * (256 - xap); + b = b * (256 - xap); + a = a * (256 - xap); + r = (r + (rr * xap)) >> 8; + g = (g + (gg * xap)) >> 8; + b = (b + (bb * xap)) >> 8; + a = (a + (aa * xap)) >> 8; + } + *dptr++ = qRgba(r >> 14, g >> 14, b >> 14, a >> 14); } - *dptr++ = qRgba(r >> 14, g >> 14, b >> 14, a >> 14); } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleAARGBA_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest, @@ -449,34 +498,55 @@ static void qt_qimageScaleAARGBA_down_x_up_y(QImageScaleInfo *isi, unsigned int int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int r, g, b, a; - qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, r, g, b, a); - - int yap = yapoints[y]; - if (yap > 0) { - int rr, gg, bb, aa; - qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa); - - r = r * (256 - yap); - g = g * (256 - yap); - b = b * (256 - yap); - a = a * (256 - yap); - r = (r + (rr * yap)) >> 8; - g = (g + (gg * yap)) >> 8; - b = (b + (bb * yap)) >> 8; - a = (a + (aa * yap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int r, g, b, a; + qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, r, g, b, a); + + int yap = yapoints[y]; + if (yap > 0) { + int rr, gg, bb, aa; + qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa); + + r = r * (256 - yap); + g = g * (256 - yap); + b = b * (256 - yap); + a = a * (256 - yap); + r = (r + (rr * yap)) >> 8; + g = (g + (gg * yap)) >> 8; + b = (b + (bb * yap)) >> 8; + a = (a + (aa * yap)) >> 8; + } + *dptr = qRgba(r >> 14, g >> 14, b >> 14, a >> 14); + dptr++; } - *dptr = qRgba(r >> 14, g >> 14, b >> 14, a >> 14); - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *dest, @@ -487,45 +557,66 @@ static void qt_qimageScaleAARGBA_down_xy(QImageScaleInfo *isi, unsigned int *des int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = (yapoints[y]) >> 16; - int yap = (yapoints[y]) & 0xffff; - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = (yapoints[y]) >> 16; + int yap = (yapoints[y]) & 0xffff; - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int rx, gx, bx, ax; - qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; - int r = ((rx>>4) * yap); - int g = ((gx>>4) * yap); - int b = ((bx>>4) * yap); - int a = ((ax>>4) * yap); + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int rx, gx, bx, ax; + qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - int j; - for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + int r = ((rx>>4) * yap); + int g = ((gx>>4) * yap); + int b = ((bx>>4) * yap); + int a = ((ax>>4) * yap); + + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + r += ((rx>>4) * Cy); + g += ((gx>>4) * Cy); + b += ((bx>>4) * Cy); + a += ((ax>>4) * Cy); + } sptr += sow; qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - r += ((rx>>4) * Cy); - g += ((gx>>4) * Cy); - b += ((bx>>4) * Cy); - a += ((ax>>4) * Cy); - } - sptr += sow; - qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - r += ((rx>>4) * j); - g += ((gx>>4) * j); - b += ((bx>>4) * j); - a += ((ax>>4) * j); + r += ((rx>>4) * j); + g += ((gx>>4) * j); + b += ((bx>>4) * j); + a += ((ax>>4) * j); - *dptr = qRgba(r >> 24, g >> 24, b >> 24, a >> 24); - dptr++; + *dptr = qRgba(r >> 24, g >> 24, b >> 24, a >> 24); + dptr++; + } } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } #if QT_CONFIG(raster_64bit) @@ -546,32 +637,53 @@ static void qt_qimageScaleRgba64_up_xy(QImageScaleInfo *isi, QRgba64 *dest, int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - const QRgba64 *sptr = ypoints[y]; - QRgba64 *dptr = dest + (y * dow); - const int yap = yapoints[y]; - if (yap > 0) { - for (int x = 0; x < dw; x++) { - const QRgba64 *pix = sptr + xpoints[x]; - const int xap = xapoints[x]; - if (xap > 0) - *dptr = interpolate_4_pixels_rgb64(pix, pix + sow, xap * 256, yap * 256); - else - *dptr = interpolate256(pix[0], 256 - yap, pix[sow], yap); - dptr++; - } - } else { - for (int x = 0; x < dw; x++) { - const QRgba64 *pix = sptr + xpoints[x]; - const int xap = xapoints[x]; - if (xap > 0) - *dptr = interpolate256(pix[0], 256 - xap, pix[1], xap); - else - *dptr = pix[0]; - dptr++; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + const QRgba64 *sptr = ypoints[y]; + QRgba64 *dptr = dest + (y * dow); + const int yap = yapoints[y]; + if (yap > 0) { + for (int x = 0; x < dw; x++) { + const QRgba64 *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = interpolate_4_pixels_rgb64(pix, pix + sow, xap * 256, yap * 256); + else + *dptr = interpolate256(pix[0], 256 - yap, pix[sow], yap); + dptr++; + } + } else { + for (int x = 0; x < dw; x++) { + const QRgba64 *pix = sptr + xpoints[x]; + const int xap = xapoints[x]; + if (xap > 0) + *dptr = interpolate256(pix[0], 256 - xap, pix[1], xap); + else + *dptr = pix[0]; + dptr++; + } } } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } void qt_qimageScaleRgba64(QImageScaleInfo *isi, QRgba64 *dest, @@ -616,33 +728,54 @@ static void qt_qimageScaleRgba64_up_x_down_y(QImageScaleInfo *isi, QRgba64 *dest int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = (yapoints[y]) >> 16; - int yap = (yapoints[y]) & 0xffff; - - QRgba64 *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const QRgba64 *sptr = ypoints[y] + xpoints[x]; - qint64 r, g, b, a; - qt_qimageScaleRgba64_helper(sptr, yap, Cy, sow, r, g, b, a); - - int xap = xapoints[x]; - if (xap > 0) { - qint64 rr, gg, bb, aa; - qt_qimageScaleRgba64_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa); - - r = r * (256 - xap); - g = g * (256 - xap); - b = b * (256 - xap); - a = a * (256 - xap); - r = (r + (rr * xap)) >> 8; - g = (g + (gg * xap)) >> 8; - b = (b + (bb * xap)) >> 8; - a = (a + (aa * xap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = (yapoints[y]) >> 16; + int yap = (yapoints[y]) & 0xffff; + + QRgba64 *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const QRgba64 *sptr = ypoints[y] + xpoints[x]; + qint64 r, g, b, a; + qt_qimageScaleRgba64_helper(sptr, yap, Cy, sow, r, g, b, a); + + int xap = xapoints[x]; + if (xap > 0) { + qint64 rr, gg, bb, aa; + qt_qimageScaleRgba64_helper(sptr + 1, yap, Cy, sow, rr, gg, bb, aa); + + r = r * (256 - xap); + g = g * (256 - xap); + b = b * (256 - xap); + a = a * (256 - xap); + r = (r + (rr * xap)) >> 8; + g = (g + (gg * xap)) >> 8; + b = (b + (bb * xap)) >> 8; + a = (a + (aa * xap)) >> 8; + } + *dptr++ = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14); } - *dptr++ = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14); } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo *isi, QRgba64 *dest, @@ -653,34 +786,55 @@ static void qt_qimageScaleRgba64_down_x_up_y(QImageScaleInfo *isi, QRgba64 *dest int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - QRgba64 *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - - const QRgba64 *sptr = ypoints[y] + xpoints[x]; - qint64 r, g, b, a; - qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, r, g, b, a); - - int yap = yapoints[y]; - if (yap > 0) { - qint64 rr, gg, bb, aa; - qt_qimageScaleRgba64_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa); - - r = r * (256 - yap); - g = g * (256 - yap); - b = b * (256 - yap); - a = a * (256 - yap); - r = (r + (rr * yap)) >> 8; - g = (g + (gg * yap)) >> 8; - b = (b + (bb * yap)) >> 8; - a = (a + (aa * yap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + QRgba64 *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const QRgba64 *sptr = ypoints[y] + xpoints[x]; + qint64 r, g, b, a; + qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, r, g, b, a); + + int yap = yapoints[y]; + if (yap > 0) { + qint64 rr, gg, bb, aa; + qt_qimageScaleRgba64_helper(sptr + sow, xap, Cx, 1, rr, gg, bb, aa); + + r = r * (256 - yap); + g = g * (256 - yap); + b = b * (256 - yap); + a = a * (256 - yap); + r = (r + (rr * yap)) >> 8; + g = (g + (gg * yap)) >> 8; + b = (b + (bb * yap)) >> 8; + a = (a + (aa * yap)) >> 8; + } + *dptr = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14); + dptr++; } - *dptr = qRgba64(r >> 14, g >> 14, b >> 14, a >> 14); - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest, @@ -691,43 +845,64 @@ static void qt_qimageScaleRgba64_down_xy(QImageScaleInfo *isi, QRgba64 *dest, int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = (yapoints[y]) >> 16; - int yap = (yapoints[y]) & 0xffff; - - QRgba64 *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - - const QRgba64 *sptr = ypoints[y] + xpoints[x]; - qint64 rx, gx, bx, ax; - qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - - qint64 r = rx * yap; - qint64 g = gx * yap; - qint64 b = bx * yap; - qint64 a = ax * yap; - int j; - for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = (yapoints[y]) >> 16; + int yap = (yapoints[y]) & 0xffff; + + QRgba64 *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const QRgba64 *sptr = ypoints[y] + xpoints[x]; + qint64 rx, gx, bx, ax; + qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + + qint64 r = rx * yap; + qint64 g = gx * yap; + qint64 b = bx * yap; + qint64 a = ax * yap; + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); + r += rx * Cy; + g += gx * Cy; + b += bx * Cy; + a += ax * Cy; + } sptr += sow; qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - r += rx * Cy; - g += gx * Cy; - b += bx * Cy; - a += ax * Cy; + r += rx * j; + g += gx * j; + b += bx * j; + a += ax * j; + + *dptr = qRgba64(r >> 28, g >> 28, b >> 28, a >> 28); + dptr++; } - sptr += sow; - qt_qimageScaleRgba64_helper(sptr, xap, Cx, 1, rx, gx, bx, ax); - r += rx * j; - g += gx * j; - b += bx * j; - a += ax * j; - - *dptr = qRgba64(r >> 28, g >> 28, b >> 28, a >> 28); - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } #endif @@ -817,31 +992,52 @@ static void qt_qimageScaleAARGB_up_x_down_y(QImageScaleInfo *isi, unsigned int * int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int r, g, b; - qt_qimageScaleAARGB_helper(sptr, yap, Cy, sow, r, g, b); - - int xap = xapoints[x]; - if (xap > 0) { - int rr, bb, gg; - qt_qimageScaleAARGB_helper(sptr + 1, yap, Cy, sow, rr, gg, bb); - - r = r * (256 - xap); - g = g * (256 - xap); - b = b * (256 - xap); - r = (r + (rr * xap)) >> 8; - g = (g + (gg * xap)) >> 8; - b = (b + (bb * xap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; + + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int r, g, b; + qt_qimageScaleAARGB_helper(sptr, yap, Cy, sow, r, g, b); + + int xap = xapoints[x]; + if (xap > 0) { + int rr, bb, gg; + qt_qimageScaleAARGB_helper(sptr + 1, yap, Cy, sow, rr, gg, bb); + + r = r * (256 - xap); + g = g * (256 - xap); + b = b * (256 - xap); + r = (r + (rr * xap)) >> 8; + g = (g + (gg * xap)) >> 8; + b = (b + (bb * xap)) >> 8; + } + *dptr++ = qRgb(r >> 14, g >> 14, b >> 14); } - *dptr++ = qRgb(r >> 14, g >> 14, b >> 14); } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleAARGB_down_x_up_y(QImageScaleInfo *isi, unsigned int *dest, @@ -853,31 +1049,52 @@ static void qt_qimageScaleAARGB_down_x_up_y(QImageScaleInfo *isi, unsigned int * int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int r, g, b; - qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, r, g, b); - - int yap = yapoints[y]; - if (yap > 0) { - int rr, bb, gg; - qt_qimageScaleAARGB_helper(sptr + sow, xap, Cx, 1, rr, gg, bb); - - r = r * (256 - yap); - g = g * (256 - yap); - b = b * (256 - yap); - r = (r + (rr * yap)) >> 8; - g = (g + (gg * yap)) >> 8; - b = (b + (bb * yap)) >> 8; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int r, g, b; + qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, r, g, b); + + int yap = yapoints[y]; + if (yap > 0) { + int rr, bb, gg; + qt_qimageScaleAARGB_helper(sptr + sow, xap, Cx, 1, rr, gg, bb); + + r = r * (256 - yap); + g = g * (256 - yap); + b = b * (256 - yap); + r = (r + (rr * yap)) >> 8; + g = (g + (gg * yap)) >> 8; + b = (b + (bb * yap)) >> 8; + } + *dptr++ = qRgb(r >> 14, g >> 14, b >> 14); } - *dptr++ = qRgb(r >> 14, g >> 14, b >> 14); } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } static void qt_qimageScaleAARGB_down_xy(QImageScaleInfo *isi, unsigned int *dest, @@ -888,43 +1105,64 @@ static void qt_qimageScaleAARGB_down_xy(QImageScaleInfo *isi, unsigned int *dest int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + int rx, gx, bx; + qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); - const unsigned int *sptr = ypoints[y] + xpoints[x]; - int rx, gx, bx; - qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); + int r = (rx >> 4) * yap; + int g = (gx >> 4) * yap; + int b = (bx >> 4) * yap; - int r = (rx >> 4) * yap; - int g = (gx >> 4) * yap; - int b = (bx >> 4) * yap; + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); - int j; - for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + r += (rx >> 4) * Cy; + g += (gx >> 4) * Cy; + b += (bx >> 4) * Cy; + } sptr += sow; qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); - r += (rx >> 4) * Cy; - g += (gx >> 4) * Cy; - b += (bx >> 4) * Cy; - } - sptr += sow; - qt_qimageScaleAARGB_helper(sptr, xap, Cx, 1, rx, gx, bx); - - r += (rx >> 4) * j; - g += (gx >> 4) * j; - b += (bx >> 4) * j; + r += (rx >> 4) * j; + g += (gx >> 4) * j; + b += (bx >> 4) * j; - *dptr = qRgb(r >> 24, g >> 24, b >> 24); - dptr++; + *dptr = qRgb(r >> 24, g >> 24, b >> 24); + dptr++; + } } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } QImage qSmoothScaleImage(const QImage &src, int dw, int dh) diff --git a/src/gui/painting/qimagescale_neon.cpp b/src/gui/painting/qimagescale_neon.cpp index 4ae113b002..416155e139 100644 --- a/src/gui/painting/qimagescale_neon.cpp +++ b/src/gui/painting/qimagescale_neon.cpp @@ -41,6 +41,11 @@ #include "qimage.h" #include <private/qsimd_p.h> +#if QT_CONFIG(thread) +#include "qsemaphore.h" +#include "qthreadpool.h" +#endif + #if defined(__ARM_NEON__) QT_BEGIN_NAMESPACE @@ -76,33 +81,54 @@ void qt_qimageScaleAARGBA_up_x_down_y_neon(QImageScaleInfo *isi, unsigned int *d int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const unsigned int *sptr = ypoints[y] + xpoints[x]; - uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow); - - int xap = xapoints[x]; - if (xap > 0) { - uint32x4_t vr = qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow); - - vx = vmulq_n_u32(vx, 256 - xap); - vr = vmulq_n_u32(vr, xap); - vx = vaddq_u32(vx, vr); - vx = vshrq_n_u32(vx, 8); + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; + + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const unsigned int *sptr = ypoints[y] + xpoints[x]; + uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow); + + int xap = xapoints[x]; + if (xap > 0) { + uint32x4_t vr = qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow); + + vx = vmulq_n_u32(vx, 256 - xap); + vr = vmulq_n_u32(vr, xap); + vx = vaddq_u32(vx, vr); + vx = vshrq_n_u32(vx, 8); + } + vx = vshrq_n_u32(vx, 14); + const uint16x4_t vx16 = vmovn_u32(vx); + const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); + *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - vx = vshrq_n_u32(vx, 14); - const uint16x4_t vx16 = vmovn_u32(vx); - const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); - *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template<bool RGB> @@ -115,33 +141,54 @@ void qt_qimageScaleAARGBA_down_x_up_y_neon(QImageScaleInfo *isi, unsigned int *d int *yapoints = isi->yapoints; /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - - const unsigned int *sptr = ypoints[y] + xpoints[x]; - uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); - - int yap = yapoints[y]; - if (yap > 0) { - uint32x4_t vr = qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1); - - vx = vmulq_n_u32(vx, 256 - yap); - vr = vmulq_n_u32(vr, yap); - vx = vaddq_u32(vx, vr); - vx = vshrq_n_u32(vx, 8); + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); + + int yap = yapoints[y]; + if (yap > 0) { + uint32x4_t vr = qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1); + + vx = vmulq_n_u32(vx, 256 - yap); + vr = vmulq_n_u32(vr, yap); + vx = vaddq_u32(vx, vr); + vx = vshrq_n_u32(vx, 8); + } + vx = vshrq_n_u32(vx, 14); + const uint16x4_t vx16 = vmovn_u32(vx); + const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); + *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - vx = vshrq_n_u32(vx, 14); - const uint16x4_t vx16 = vmovn_u32(vx); - const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); - *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template<bool RGB> @@ -153,43 +200,64 @@ void qt_qimageScaleAARGBA_down_xy_neon(QImageScaleInfo *isi, unsigned int *dest, int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const int Cx = xapoints[x] >> 16; - const int xap = xapoints[x] & 0xffff; + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const int Cx = xapoints[x] >> 16; + const int xap = xapoints[x] & 0xffff; - const unsigned int *sptr = ypoints[y] + xpoints[x]; - uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); - vx = vshrq_n_u32(vx, 4); - uint32x4_t vr = vmulq_n_u32(vx, yap); + const unsigned int *sptr = ypoints[y] + xpoints[x]; + uint32x4_t vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); + vx = vshrq_n_u32(vx, 4); + uint32x4_t vr = vmulq_n_u32(vx, yap); - int j; - for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); + vx = vshrq_n_u32(vx, 4); + vx = vmulq_n_u32(vx, Cy); + vr = vaddq_u32(vr, vx); + } sptr += sow; vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); vx = vshrq_n_u32(vx, 4); - vx = vmulq_n_u32(vx, Cy); + vx = vmulq_n_u32(vx, j); vr = vaddq_u32(vr, vx); + + vx = vshrq_n_u32(vr, 24); + const uint16x4_t vx16 = vmovn_u32(vx); + const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); + *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - sptr += sow; - vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1); - vx = vshrq_n_u32(vx, 4); - vx = vmulq_n_u32(vx, j); - vr = vaddq_u32(vr, vx); - - vx = vshrq_n_u32(vr, 24); - const uint16x4_t vx16 = vmovn_u32(vx); - const uint8x8_t vx8 = vmovn_u16(vcombine_u16(vx16, vx16)); - *dptr = vget_lane_u32(vreinterpret_u32_u8(vx8), 0); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template void qt_qimageScaleAARGBA_up_x_down_y_neon<false>(QImageScaleInfo *isi, unsigned int *dest, diff --git a/src/gui/painting/qimagescale_p.h b/src/gui/painting/qimagescale_p.h index 244d681718..a9a4c0f858 100644 --- a/src/gui/painting/qimagescale_p.h +++ b/src/gui/painting/qimagescale_p.h @@ -66,6 +66,8 @@ namespace QImageScale { int *xapoints{nullptr}; int *yapoints{nullptr}; int xup_yup{0}; + int sh = 0; + int sw = 0; }; } diff --git a/src/gui/painting/qimagescale_sse4.cpp b/src/gui/painting/qimagescale_sse4.cpp index 5861a2e2ff..902ae61ed2 100644 --- a/src/gui/painting/qimagescale_sse4.cpp +++ b/src/gui/painting/qimagescale_sse4.cpp @@ -42,6 +42,11 @@ #include <private/qdrawhelper_x86_p.h> #include <private/qsimd_p.h> +#if QT_CONFIG(thread) +#include "qsemaphore.h" +#include "qthreadpool.h" +#endif + #if defined(QT_COMPILER_SUPPORTS_SSE4_1) QT_BEGIN_NAMESPACE @@ -70,44 +75,65 @@ void qt_qimageScaleAARGBA_up_x_down_y_sse4(QImageScaleInfo *isi, unsigned int *d int dw, int dh, int dow, int sow) { const unsigned int **ypoints = isi->ypoints; - int *xpoints = isi->xpoints; - int *xapoints = isi->xapoints; - int *yapoints = isi->yapoints; + const int *xpoints = isi->xpoints; + const int *xapoints = isi->xapoints; + const int *yapoints = isi->yapoints; const __m128i v256 = _mm_set1_epi32(256); /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; - const __m128i vCy = _mm_set1_epi32(Cy); - const __m128i vyap = _mm_set1_epi32(yap); - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const unsigned int *sptr = ypoints[y] + xpoints[x]; - __m128i vx = qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow, vyap, vCy); - - int xap = xapoints[x]; - if (xap > 0) { - const __m128i vxap = _mm_set1_epi32(xap); - const __m128i vinvxap = _mm_sub_epi32(v256, vxap); - __m128i vr = qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow, vyap, vCy); + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + const int Cy = yapoints[y] >> 16; + const int yap = yapoints[y] & 0xffff; + const __m128i vCy = _mm_set1_epi32(Cy); + const __m128i vyap = _mm_set1_epi32(yap); - vx = _mm_mullo_epi32(vx, vinvxap); - vr = _mm_mullo_epi32(vr, vxap); - vx = _mm_add_epi32(vx, vr); - vx = _mm_srli_epi32(vx, 8); + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const unsigned int *sptr = ypoints[y] + xpoints[x]; + __m128i vx = qt_qimageScaleAARGBA_helper(sptr, yap, Cy, sow, vyap, vCy); + + const int xap = xapoints[x]; + if (xap > 0) { + const __m128i vxap = _mm_set1_epi32(xap); + const __m128i vinvxap = _mm_sub_epi32(v256, vxap); + __m128i vr = qt_qimageScaleAARGBA_helper(sptr + 1, yap, Cy, sow, vyap, vCy); + + vx = _mm_mullo_epi32(vx, vinvxap); + vr = _mm_mullo_epi32(vr, vxap); + vx = _mm_add_epi32(vx, vr); + vx = _mm_srli_epi32(vx, 8); + } + vx = _mm_srli_epi32(vx, 14); + vx = _mm_packus_epi32(vx, vx); + vx = _mm_packus_epi16(vx, vx); + *dptr = _mm_cvtsi128_si32(vx); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - vx = _mm_srli_epi32(vx, 14); - vx = _mm_packus_epi32(vx, _mm_setzero_si128()); - vx = _mm_packus_epi16(vx, _mm_setzero_si128()); - *dptr = _mm_cvtsi128_si32(vx); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template<bool RGB> @@ -122,37 +148,58 @@ void qt_qimageScaleAARGBA_down_x_up_y_sse4(QImageScaleInfo *isi, unsigned int *d const __m128i v256 = _mm_set1_epi32(256); /* go through every scanline in the output buffer */ - for (int y = 0; y < dh; y++) { - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - int Cx = xapoints[x] >> 16; - int xap = xapoints[x] & 0xffff; - const __m128i vCx = _mm_set1_epi32(Cx); - const __m128i vxap = _mm_set1_epi32(xap); - - const unsigned int *sptr = ypoints[y] + xpoints[x]; - __m128i vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); - - int yap = yapoints[y]; - if (yap > 0) { - const __m128i vyap = _mm_set1_epi32(yap); - const __m128i vinvyap = _mm_sub_epi32(v256, vyap); - __m128i vr = qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1, vxap, vCx); - - vx = _mm_mullo_epi32(vx, vinvyap); - vr = _mm_mullo_epi32(vr, vyap); - vx = _mm_add_epi32(vx, vr); - vx = _mm_srli_epi32(vx, 8); + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + int Cx = xapoints[x] >> 16; + int xap = xapoints[x] & 0xffff; + const __m128i vCx = _mm_set1_epi32(Cx); + const __m128i vxap = _mm_set1_epi32(xap); + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + __m128i vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); + + int yap = yapoints[y]; + if (yap > 0) { + const __m128i vyap = _mm_set1_epi32(yap); + const __m128i vinvyap = _mm_sub_epi32(v256, vyap); + __m128i vr = qt_qimageScaleAARGBA_helper(sptr + sow, xap, Cx, 1, vxap, vCx); + + vx = _mm_mullo_epi32(vx, vinvyap); + vr = _mm_mullo_epi32(vr, vyap); + vx = _mm_add_epi32(vx, vr); + vx = _mm_srli_epi32(vx, 8); + } + vx = _mm_srli_epi32(vx, 14); + vx = _mm_packus_epi32(vx, vx); + vx = _mm_packus_epi16(vx, vx); + *dptr = _mm_cvtsi128_si32(vx); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - vx = _mm_srli_epi32(vx, 14); - vx = _mm_packus_epi32(vx, _mm_setzero_si128()); - vx = _mm_packus_epi16(vx, _mm_setzero_si128()); - *dptr = _mm_cvtsi128_si32(vx); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template<bool RGB> @@ -164,42 +211,63 @@ void qt_qimageScaleAARGBA_down_xy_sse4(QImageScaleInfo *isi, unsigned int *dest, int *xapoints = isi->xapoints; int *yapoints = isi->yapoints; - for (int y = 0; y < dh; y++) { - int Cy = yapoints[y] >> 16; - int yap = yapoints[y] & 0xffff; - const __m128i vCy = _mm_set1_epi32(Cy); - const __m128i vyap = _mm_set1_epi32(yap); - - unsigned int *dptr = dest + (y * dow); - for (int x = 0; x < dw; x++) { - const int Cx = xapoints[x] >> 16; - const int xap = xapoints[x] & 0xffff; - const __m128i vCx = _mm_set1_epi32(Cx); - const __m128i vxap = _mm_set1_epi32(xap); - - const unsigned int *sptr = ypoints[y] + xpoints[x]; - __m128i vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); - __m128i vr = _mm_mullo_epi32(_mm_srli_epi32(vx, 4), vyap); - - int j; - for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + auto scaleSection = [&] (int yStart, int yEnd) { + for (int y = yStart; y < yEnd; ++y) { + int Cy = yapoints[y] >> 16; + int yap = yapoints[y] & 0xffff; + const __m128i vCy = _mm_set1_epi32(Cy); + const __m128i vyap = _mm_set1_epi32(yap); + + unsigned int *dptr = dest + (y * dow); + for (int x = 0; x < dw; x++) { + const int Cx = xapoints[x] >> 16; + const int xap = xapoints[x] & 0xffff; + const __m128i vCx = _mm_set1_epi32(Cx); + const __m128i vxap = _mm_set1_epi32(xap); + + const unsigned int *sptr = ypoints[y] + xpoints[x]; + __m128i vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); + __m128i vr = _mm_mullo_epi32(_mm_srli_epi32(vx, 4), vyap); + + int j; + for (j = (1 << 14) - yap; j > Cy; j -= Cy) { + sptr += sow; + vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); + vr = _mm_add_epi32(vr, _mm_mullo_epi32(_mm_srli_epi32(vx, 4), vCy)); + } sptr += sow; vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); - vr = _mm_add_epi32(vr, _mm_mullo_epi32(_mm_srli_epi32(vx, 4), vCy)); + vr = _mm_add_epi32(vr, _mm_mullo_epi32(_mm_srli_epi32(vx, 4), _mm_set1_epi32(j))); + + vr = _mm_srli_epi32(vr, 24); + vr = _mm_packus_epi32(vr, _mm_setzero_si128()); + vr = _mm_packus_epi16(vr, _mm_setzero_si128()); + *dptr = _mm_cvtsi128_si32(vr); + if (RGB) + *dptr |= 0xff000000; + dptr++; } - sptr += sow; - vx = qt_qimageScaleAARGBA_helper(sptr, xap, Cx, 1, vxap, vCx); - vr = _mm_add_epi32(vr, _mm_mullo_epi32(_mm_srli_epi32(vx, 4), _mm_set1_epi32(j))); - - vr = _mm_srli_epi32(vr, 24); - vr = _mm_packus_epi32(vr, _mm_setzero_si128()); - vr = _mm_packus_epi16(vr, _mm_setzero_si128()); - *dptr = _mm_cvtsi128_si32(vr); - if (RGB) - *dptr |= 0xff000000; - dptr++; } + }; +#if QT_CONFIG(thread) + int segments = (qsizetype(isi->sh) * isi->sw) / (1<<16); + segments = std::min(segments, dh); + if (segments > 1) { + QSemaphore semaphore; + int y = 0; + for (int i = 0; i < segments; ++i) { + int yn = (dh - y) / (segments - i); + QThreadPool::globalInstance()->start([&, y, yn]() { + scaleSection(y, y + yn); + semaphore.release(1); + }); + y += yn; + } + semaphore.acquire(segments); + return; } +#endif + scaleSection(0, dh); } template void qt_qimageScaleAARGBA_up_x_down_y_sse4<false>(QImageScaleInfo *isi, unsigned int *dest, diff --git a/src/gui/painting/qpainterpath.cpp b/src/gui/painting/qpainterpath.cpp index 11623c78f0..e3cdc218b2 100644 --- a/src/gui/painting/qpainterpath.cpp +++ b/src/gui/painting/qpainterpath.cpp @@ -1603,25 +1603,6 @@ QPainterPath QPainterPath::toReversed() const } /*! - \overload - - Converts the path into a list of polygons without any transformation, - and returns the list. - - This function creates one polygon for each subpath regardless of - intersecting subpaths (i.e. overlapping bounding rectangles). To - make sure that such overlapping subpaths are filled correctly, use - the toFillPolygons() function instead. - - \sa toFillPolygons(), toFillPolygon(), {QPainterPath#QPainterPath - Conversion}{QPainterPath Conversion} -*/ -QList<QPolygonF> QPainterPath::toSubpathPolygons() const -{ - return toSubpathPolygons(QTransform()); -} - -/*! Converts the path into a list of polygons using the QTransform \a matrix, and returns the list. @@ -1679,35 +1660,6 @@ QList<QPolygonF> QPainterPath::toSubpathPolygons(const QTransform &matrix) const } /*! - \overload - - Converts the path into a list of polygons without any transformation, - and returns the list. - - The function differs from the toFillPolygon() function in that it - creates several polygons. It is provided because it is usually - faster to draw several small polygons than to draw one large - polygon, even though the total number of points drawn is the same. - - The toFillPolygons() function differs from the toSubpathPolygons() - function in that it create only polygon for subpaths that have - overlapping bounding rectangles. - - Like the toFillPolygon() function, this function uses a rewinding - technique to make sure that overlapping subpaths can be filled - using the correct fill rule. Note that rewinding inserts addition - lines in the polygons so the outline of the fill polygon does not - match the outline of the path. - - \sa toSubpathPolygons(), toFillPolygon(), - {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion} -*/ -QList<QPolygonF> QPainterPath::toFillPolygons() const -{ - return toFillPolygons(QTransform()); -} - -/*! Converts the path into a list of polygons using the QTransform \a matrix, and returns the list. @@ -2903,28 +2855,6 @@ void QPainterPathStroker::setDashOffset(qreal offset) } /*! - \overload - - Converts the path into a polygon without any transformation, - and returns the polygon. - - The polygon is created by first converting all subpaths to - polygons, then using a rewinding technique to make sure that - overlapping subpaths can be filled using the correct fill rule. - - Note that rewinding inserts addition lines in the polygon so - the outline of the fill polygon does not match the outline of - the path. - - \sa toSubpathPolygons(), toFillPolygons(), - {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion} -*/ -QPolygonF QPainterPath::toFillPolygon() const -{ - return toFillPolygon(QTransform()); -} - -/*! Converts the path into a polygon using the QTransform \a matrix, and returns the polygon. diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h index 078b665222..aa43c74bee 100644 --- a/src/gui/painting/qpainterpath.h +++ b/src/gui/painting/qpainterpath.h @@ -41,6 +41,7 @@ #define QPAINTERPATH_H #include <QtGui/qtguiglobal.h> +#include <QtGui/qtransform.h> #include <QtCore/qglobal.h> #include <QtCore/qrect.h> #include <QtCore/qline.h> @@ -176,12 +177,9 @@ public: Q_REQUIRED_RESULT QPainterPath toReversed() const; - QList<QPolygonF> toSubpathPolygons() const; - QList<QPolygonF> toSubpathPolygons(const QTransform &matrix) const; - QList<QPolygonF> toFillPolygons() const; - QList<QPolygonF> toFillPolygons(const QTransform &matrix) const; - QPolygonF toFillPolygon() const; - QPolygonF toFillPolygon(const QTransform &matrix) const; + QList<QPolygonF> toSubpathPolygons(const QTransform &matrix = QTransform()) const; + QList<QPolygonF> toFillPolygons(const QTransform &matrix = QTransform()) const; + QPolygonF toFillPolygon(const QTransform &matrix = QTransform()) const; int elementCount() const; QPainterPath::Element elementAt(int i) const; @@ -356,6 +354,8 @@ inline void QPainterPath::translate(const QPointF &offset) inline QPainterPath QPainterPath::translated(const QPointF &offset) const { return translated(offset.x(), offset.y()); } +inline QPainterPath operator *(const QPainterPath &p, const QTransform &m) +{ return m.map(p); } #ifndef QT_NO_DEBUG_STREAM Q_GUI_EXPORT QDebug operator<<(QDebug, const QPainterPath &); diff --git a/src/gui/painting/qrasterizer.cpp b/src/gui/painting/qrasterizer.cpp index cd31d75f83..f3c193d799 100644 --- a/src/gui/painting/qrasterizer.cpp +++ b/src/gui/painting/qrasterizer.cpp @@ -46,6 +46,8 @@ #include <private/qdatabuffer_p.h> #include <private/qdrawhelper_p.h> +#include <QtGui/qpainterpath.h> + #include <algorithm> QT_BEGIN_NAMESPACE diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index e906397520..3728060caf 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -43,6 +43,8 @@ #include "private/qfontengine_p.h" #include "private/qnumeric_p.h" +#include <QtGui/qpainterpath.h> + QT_BEGIN_NAMESPACE // #define CACHE_DEBUG diff --git a/src/gui/painting/qtransform.h b/src/gui/painting/qtransform.h index 351f2b2e4e..d235f15029 100644 --- a/src/gui/painting/qtransform.h +++ b/src/gui/painting/qtransform.h @@ -40,7 +40,6 @@ #define QTRANSFORM_H #include <QtGui/qtguiglobal.h> -#include <QtGui/qpainterpath.h> #include <QtGui/qpolygon.h> #include <QtGui/qregion.h> #include <QtGui/qwindowdefs.h> @@ -50,8 +49,8 @@ QT_BEGIN_NAMESPACE - class QVariant; +class QPainterPath; class Q_GUI_EXPORT QTransform { @@ -406,8 +405,6 @@ inline QPolygonF operator *(const QPolygonF &a, const QTransform &m) { return m.map(a); } inline QRegion operator *(const QRegion &r, const QTransform &m) { return m.map(r); } -inline QPainterPath operator *(const QPainterPath &p, const QTransform &m) -{ return m.map(p); } inline QTransform operator *(const QTransform &a, qreal n) { QTransform t(a); t *= n; return t; } diff --git a/src/gui/rhi/qrhi.cpp b/src/gui/rhi/qrhi.cpp index c805e23ad0..83c1e8eaa2 100644 --- a/src/gui/rhi/qrhi.cpp +++ b/src/gui/rhi/qrhi.cpp @@ -2885,16 +2885,57 @@ QRhiShaderResourceBinding QRhiShaderResourceBinding::uniformBufferWithDynamicOff \return a shader resource binding for the given binding number, pipeline stages, texture, and sampler specified by \a binding, \a stage, \a tex, \a sampler. + + \note This function is equivalent to calling sampledTextures() with a + \c count of 1. + + \sa sampledTextures() */ QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTexture( int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler) { + const TextureAndSampler texSampler = { tex, sampler }; + return sampledTextures(binding, stage, 1, &texSampler); +} + +/*! + \return a shader resource binding for the given binding number, pipeline + stages, and the array of texture-sampler pairs specified by \a binding, \a + stage, \a count, and \a texSamplers. + + \note \a count must be at least 1, and not larger than 16. + + \note When \a count is 1, this function is equivalent to sampledTexture(). + + This function is relevant when arrays of combined image samplers are + involved. For example, in GLSL \c{layout(binding = 5) uniform sampler2D + shadowMaps[8];} declares an array of combined image samplers. The + application is then expected provide a QRhiShaderResourceBinding for + binding point 5, set up by calling this function with \a count set to 8 and + a valid texture and sampler for each element of the array. + + \warning All elements of the array must be specified. With the above + example, the only valid, portable approach is calling this function with a + \a count of 8. Additionally, all QRhiTexture and QRhiSampler instances must + be valid, meaning nullptr is not an accepted value. This is due to some of + the underlying APIs, such as, Vulkan, that require a valid image and + sampler object for each element in descriptor arrays. Applications are + advised to provide "dummy" samplers and textures if some array elements are + not relevant (due to not being accessed in the shader). + + \sa sampledTexture() + */ +QRhiShaderResourceBinding QRhiShaderResourceBinding::sampledTextures( + int binding, StageFlags stage, int count, const TextureAndSampler *texSamplers) +{ + Q_ASSERT(count >= 1 && count <= Data::MAX_TEX_SAMPLER_ARRAY_SIZE); QRhiShaderResourceBinding b; b.d.binding = binding; b.d.stage = stage; b.d.type = SampledTexture; - b.d.u.stex.tex = tex; - b.d.u.stex.sampler = sampler; + b.d.u.stex.count = count; + for (int i = 0; i < count; ++i) + b.d.u.stex.texSamplers[i] = texSamplers[i]; return b; } @@ -3084,10 +3125,14 @@ bool operator==(const QRhiShaderResourceBinding &a, const QRhiShaderResourceBind } break; case QRhiShaderResourceBinding::SampledTexture: - if (da->u.stex.tex != db->u.stex.tex - || da->u.stex.sampler != db->u.stex.sampler) - { + if (da->u.stex.count != db->u.stex.count) return false; + for (int i = 0; i < da->u.stex.count; ++i) { + if (da->u.stex.texSamplers[i].tex != db->u.stex.texSamplers[i].tex + || da->u.stex.texSamplers[i].sampler != db->u.stex.texSamplers[i].sampler) + { + return false; + } } break; case QRhiShaderResourceBinding::ImageLoad: @@ -3162,10 +3207,13 @@ QDebug operator<<(QDebug dbg, const QRhiShaderResourceBinding &b) << ')'; break; case QRhiShaderResourceBinding::SampledTexture: - dbg.nospace() << " SampledTexture(" - << "texture=" << d->u.stex.tex - << " sampler=" << d->u.stex.sampler - << ')'; + dbg.nospace() << " SampledTextures(" + << "count=" << d->u.stex.count; + for (int i = 0; i < d->u.stex.count; ++i) { + dbg.nospace() << " texture=" << d->u.stex.texSamplers[i].tex + << " sampler=" << d->u.stex.texSamplers[i].sampler; + } + dbg.nospace() << ')'; break; case QRhiShaderResourceBinding::ImageLoad: dbg.nospace() << " ImageLoad(" diff --git a/src/gui/rhi/qrhi_p.h b/src/gui/rhi/qrhi_p.h index 17c911a5ff..9d906d7bbd 100644 --- a/src/gui/rhi/qrhi_p.h +++ b/src/gui/rhi/qrhi_p.h @@ -348,6 +348,12 @@ public: static QRhiShaderResourceBinding sampledTexture(int binding, StageFlags stage, QRhiTexture *tex, QRhiSampler *sampler); + struct TextureAndSampler { + QRhiTexture *tex; + QRhiSampler *sampler; + }; + static QRhiShaderResourceBinding sampledTextures(int binding, StageFlags stage, int count, const TextureAndSampler *texSamplers); + static QRhiShaderResourceBinding imageLoad(int binding, StageFlags stage, QRhiTexture *tex, int level); static QRhiShaderResourceBinding imageStore(int binding, StageFlags stage, QRhiTexture *tex, int level); static QRhiShaderResourceBinding imageLoadStore(int binding, StageFlags stage, QRhiTexture *tex, int level); @@ -370,9 +376,10 @@ public: int maybeSize; bool hasDynamicOffset; }; + static const int MAX_TEX_SAMPLER_ARRAY_SIZE = 16; struct SampledTextureData { - QRhiTexture *tex; - QRhiSampler *sampler; + int count; + TextureAndSampler texSamplers[MAX_TEX_SAMPLER_ARRAY_SIZE]; }; struct StorageImageData { QRhiTexture *tex; diff --git a/src/gui/rhi/qrhid3d11.cpp b/src/gui/rhi/qrhid3d11.cpp index 7b583e6fd2..ed202958f3 100644 --- a/src/gui/rhi/qrhid3d11.cpp +++ b/src/gui/rhi/qrhid3d11.cpp @@ -113,6 +113,10 @@ QT_BEGIN_NAMESPACE #define DXGI_ADAPTER_FLAG_SOFTWARE 2 #endif +#ifndef D3D11_1_UAV_SLOT_COUNT +#define D3D11_1_UAV_SLOT_COUNT 64 +#endif + QRhiD3D11::QRhiD3D11(QRhiD3D11InitParams *params, QRhiD3D11NativeHandles *importDevice) : ofr(this), deviceCurse(this) @@ -627,18 +631,25 @@ void QRhiD3D11::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind break; case QRhiShaderResourceBinding::SampledTexture: { - QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex); - QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler); - if (texD->generation != bd.stex.texGeneration - || texD->m_id != bd.stex.texId - || samplerD->generation != bd.stex.samplerGeneration - || samplerD->m_id != bd.stex.samplerId) - { + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + if (bd.stex.count != data->count) { + bd.stex.count = data->count; srbUpdate = true; - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; + } + for (int elem = 0; elem < data->count; ++elem) { + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, data->texSamplers[elem].tex); + QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, data->texSamplers[elem].sampler); + if (texD->generation != bd.stex.d[elem].texGeneration + || texD->m_id != bd.stex.d[elem].texId + || samplerD->generation != bd.stex.d[elem].samplerGeneration + || samplerD->m_id != bd.stex.d[elem].samplerId) + { + srbUpdate = true; + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + } } } break; @@ -1894,31 +1905,38 @@ void QRhiD3D11::updateShaderResourceBindings(QD3D11ShaderResourceBindings *srbD, break; case QRhiShaderResourceBinding::SampledTexture: { - QD3D11Texture *texD = QRHI_RES(QD3D11Texture, b->u.stex.tex); - QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, b->u.stex.sampler); - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; - if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { - QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps); - if (nativeBinding.first >= 0 && nativeBinding.second >= 0) { - res[RBM_VERTEX].textures.append({ nativeBinding.first, texD->srv }); - res[RBM_VERTEX].samplers.append({ nativeBinding.second, samplerD->samplerState }); + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + bd.stex.count = data->count; + const QPair<int, int> nativeBindingVert = mapBinding(b->binding, RBM_VERTEX, nativeResourceBindingMaps); + const QPair<int, int> nativeBindingFrag = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps); + const QPair<int, int> nativeBindingComp = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps); + // if SPIR-V binding b is mapped to tN and sN in HLSL, and it + // is an array, then it will use tN, tN+1, tN+2, ..., and sN, + // sN+1, sN+2, ... + for (int elem = 0; elem < data->count; ++elem) { + QD3D11Texture *texD = QRHI_RES(QD3D11Texture, data->texSamplers[elem].tex); + QD3D11Sampler *samplerD = QRHI_RES(QD3D11Sampler, data->texSamplers[elem].sampler); + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { + if (nativeBindingVert.first >= 0 && nativeBindingVert.second >= 0) { + res[RBM_VERTEX].textures.append({ nativeBindingVert.first + elem, texD->srv }); + res[RBM_VERTEX].samplers.append({ nativeBindingVert.second + elem, samplerD->samplerState }); + } } - } - if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { - QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_FRAGMENT, nativeResourceBindingMaps); - if (nativeBinding.first >= 0 && nativeBinding.second >= 0) { - res[RBM_FRAGMENT].textures.append({ nativeBinding.first, texD->srv }); - res[RBM_FRAGMENT].samplers.append({ nativeBinding.second, samplerD->samplerState }); + if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { + if (nativeBindingFrag.first >= 0 && nativeBindingFrag.second >= 0) { + res[RBM_FRAGMENT].textures.append({ nativeBindingFrag.first + elem, texD->srv }); + res[RBM_FRAGMENT].samplers.append({ nativeBindingFrag.second + elem, samplerD->samplerState }); + } } - } - if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { - QPair<int, int> nativeBinding = mapBinding(b->binding, RBM_COMPUTE, nativeResourceBindingMaps); - if (nativeBinding.first >= 0 && nativeBinding.second >= 0) { - res[RBM_COMPUTE].textures.append({ nativeBinding.first, texD->srv }); - res[RBM_COMPUTE].samplers.append({ nativeBinding.second, samplerD->samplerState }); + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { + if (nativeBindingComp.first >= 0 && nativeBindingComp.second >= 0) { + res[RBM_COMPUTE].textures.append({ nativeBindingComp.first + elem, texD->srv }); + res[RBM_COMPUTE].samplers.append({ nativeBindingComp.second + elem, samplerD->samplerState }); + } } } } @@ -2077,102 +2095,156 @@ static void applyDynamicOffsets(QVarLengthArray<UINT, 4> *offsets, } } +static inline uint clampedResourceCount(uint startSlot, int countSlots, uint maxSlots, const char *resType) +{ + if (startSlot + countSlots > maxSlots) { + qWarning("Not enough D3D11 %s slots to bind %d resources starting at slot %d, max slots is %d", + resType, countSlots, startSlot, maxSlots); + countSlots = maxSlots > startSlot ? maxSlots - startSlot : 0; + } + return countSlots; +} + void QRhiD3D11::bindShaderResources(QD3D11ShaderResourceBindings *srbD, const uint *dynOfsPairs, int dynOfsPairCount, bool offsetOnlyChange) { if (!offsetOnlyChange) { - for (const auto &batch : srbD->vssamplers.batches) - context->VSSetSamplers(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); + for (const auto &batch : srbD->vssamplers.batches) { + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "VS sampler"); + if (count) + context->VSSetSamplers(batch.startBinding, count, batch.resources.constData()); + } for (const auto &batch : srbD->vsshaderresources.batches) { - context->VSSetShaderResources(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); - contextState.vsHighestActiveSrvBinding = qMax<int>(contextState.vsHighestActiveSrvBinding, - int(batch.startBinding) + batch.resources.count() - 1); + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "VS SRV"); + if (count) { + context->VSSetShaderResources(batch.startBinding, count, batch.resources.constData()); + contextState.vsHighestActiveSrvBinding = qMax(contextState.vsHighestActiveSrvBinding, + int(batch.startBinding + count) - 1); + } } - for (const auto &batch : srbD->fssamplers.batches) - context->PSSetSamplers(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); + for (const auto &batch : srbD->fssamplers.batches) { + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "PS sampler"); + if (count) + context->PSSetSamplers(batch.startBinding, count, batch.resources.constData()); + } for (const auto &batch : srbD->fsshaderresources.batches) { - context->PSSetShaderResources(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); - contextState.fsHighestActiveSrvBinding = qMax<int>(contextState.fsHighestActiveSrvBinding, - int(batch.startBinding) + batch.resources.count() - 1); + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "PS SRV"); + if (count) { + context->PSSetShaderResources(batch.startBinding, count, batch.resources.constData()); + contextState.fsHighestActiveSrvBinding = qMax(contextState.fsHighestActiveSrvBinding, + int(batch.startBinding + count) - 1); + } } - for (const auto &batch : srbD->cssamplers.batches) - context->CSSetSamplers(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); + for (const auto &batch : srbD->cssamplers.batches) { + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_SAMPLER_SLOT_COUNT, "CS sampler"); + if (count) + context->CSSetSamplers(batch.startBinding, count, batch.resources.constData()); + } for (const auto &batch : srbD->csshaderresources.batches) { - context->CSSetShaderResources(batch.startBinding, UINT(batch.resources.count()), batch.resources.constData()); - contextState.csHighestActiveSrvBinding = qMax<int>(contextState.csHighestActiveSrvBinding, - int(batch.startBinding) + batch.resources.count() - 1); + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT, "CS SRV"); + if (count) { + context->CSSetShaderResources(batch.startBinding, count, batch.resources.constData()); + contextState.csHighestActiveSrvBinding = qMax(contextState.csHighestActiveSrvBinding, + int(batch.startBinding + count) - 1); + } } } for (int i = 0, ie = srbD->vsubufs.batches.count(); i != ie; ++i) { - if (!dynOfsPairCount) { - context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding, - UINT(srbD->vsubufs.batches[i].resources.count()), - srbD->vsubufs.batches[i].resources.constData(), - srbD->vsubufoffsets.batches[i].resources.constData(), - srbD->vsubufsizes.batches[i].resources.constData()); - } else { - QVarLengthArray<UINT, 4> offsets; - applyDynamicOffsets(&offsets, i, &srbD->vsubufs, &srbD->vsubufoffsets, dynOfsPairs, dynOfsPairCount); - context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding, - UINT(srbD->vsubufs.batches[i].resources.count()), - srbD->vsubufs.batches[i].resources.constData(), - offsets.constData(), - srbD->vsubufsizes.batches[i].resources.constData()); + const uint count = clampedResourceCount(srbD->vsubufs.batches[i].startBinding, + srbD->vsubufs.batches[i].resources.count(), + D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT, + "VS cbuf"); + if (count) { + if (!dynOfsPairCount) { + context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding, + count, + srbD->vsubufs.batches[i].resources.constData(), + srbD->vsubufoffsets.batches[i].resources.constData(), + srbD->vsubufsizes.batches[i].resources.constData()); + } else { + QVarLengthArray<UINT, 4> offsets; + applyDynamicOffsets(&offsets, i, &srbD->vsubufs, &srbD->vsubufoffsets, dynOfsPairs, dynOfsPairCount); + context->VSSetConstantBuffers1(srbD->vsubufs.batches[i].startBinding, + count, + srbD->vsubufs.batches[i].resources.constData(), + offsets.constData(), + srbD->vsubufsizes.batches[i].resources.constData()); + } } } for (int i = 0, ie = srbD->fsubufs.batches.count(); i != ie; ++i) { - if (!dynOfsPairCount) { - context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding, - UINT(srbD->fsubufs.batches[i].resources.count()), - srbD->fsubufs.batches[i].resources.constData(), - srbD->fsubufoffsets.batches[i].resources.constData(), - srbD->fsubufsizes.batches[i].resources.constData()); - } else { - QVarLengthArray<UINT, 4> offsets; - applyDynamicOffsets(&offsets, i, &srbD->fsubufs, &srbD->fsubufoffsets, dynOfsPairs, dynOfsPairCount); - context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding, - UINT(srbD->fsubufs.batches[i].resources.count()), - srbD->fsubufs.batches[i].resources.constData(), - offsets.constData(), - srbD->fsubufsizes.batches[i].resources.constData()); + const uint count = clampedResourceCount(srbD->fsubufs.batches[i].startBinding, + srbD->fsubufs.batches[i].resources.count(), + D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT, + "PS cbuf"); + if (count) { + if (!dynOfsPairCount) { + context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding, + count, + srbD->fsubufs.batches[i].resources.constData(), + srbD->fsubufoffsets.batches[i].resources.constData(), + srbD->fsubufsizes.batches[i].resources.constData()); + } else { + QVarLengthArray<UINT, 4> offsets; + applyDynamicOffsets(&offsets, i, &srbD->fsubufs, &srbD->fsubufoffsets, dynOfsPairs, dynOfsPairCount); + context->PSSetConstantBuffers1(srbD->fsubufs.batches[i].startBinding, + count, + srbD->fsubufs.batches[i].resources.constData(), + offsets.constData(), + srbD->fsubufsizes.batches[i].resources.constData()); + } } } for (int i = 0, ie = srbD->csubufs.batches.count(); i != ie; ++i) { - if (!dynOfsPairCount) { - context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding, - UINT(srbD->csubufs.batches[i].resources.count()), - srbD->csubufs.batches[i].resources.constData(), - srbD->csubufoffsets.batches[i].resources.constData(), - srbD->csubufsizes.batches[i].resources.constData()); - } else { - QVarLengthArray<UINT, 4> offsets; - applyDynamicOffsets(&offsets, i, &srbD->csubufs, &srbD->csubufoffsets, dynOfsPairs, dynOfsPairCount); - context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding, - UINT(srbD->csubufs.batches[i].resources.count()), - srbD->csubufs.batches[i].resources.constData(), - offsets.constData(), - srbD->csubufsizes.batches[i].resources.constData()); + const uint count = clampedResourceCount(srbD->csubufs.batches[i].startBinding, + srbD->csubufs.batches[i].resources.count(), + D3D11_COMMONSHADER_CONSTANT_BUFFER_API_SLOT_COUNT, + "CS cbuf"); + if (count) { + if (!dynOfsPairCount) { + context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding, + count, + srbD->csubufs.batches[i].resources.constData(), + srbD->csubufoffsets.batches[i].resources.constData(), + srbD->csubufsizes.batches[i].resources.constData()); + } else { + QVarLengthArray<UINT, 4> offsets; + applyDynamicOffsets(&offsets, i, &srbD->csubufs, &srbD->csubufoffsets, dynOfsPairs, dynOfsPairCount); + context->CSSetConstantBuffers1(srbD->csubufs.batches[i].startBinding, + count, + srbD->csubufs.batches[i].resources.constData(), + offsets.constData(), + srbD->csubufsizes.batches[i].resources.constData()); + } } } - for (int i = 0, ie = srbD->csUAVs.batches.count(); i != ie; ++i) { - const uint startBinding = srbD->csUAVs.batches[i].startBinding; - const uint count = uint(srbD->csUAVs.batches[i].resources.count()); - context->CSSetUnorderedAccessViews(startBinding, - count, - srbD->csUAVs.batches[i].resources.constData(), - nullptr); - contextState.csHighestActiveUavBinding = qMax<int>(contextState.csHighestActiveUavBinding, - int(startBinding + count - 1)); + for (const auto &batch : srbD->csUAVs.batches) { + const uint count = clampedResourceCount(batch.startBinding, batch.resources.count(), + D3D11_1_UAV_SLOT_COUNT, "CS UAV"); + if (count) { + context->CSSetUnorderedAccessViews(batch.startBinding, + count, + batch.resources.constData(), + nullptr); + contextState.csHighestActiveUavBinding = qMax(contextState.csHighestActiveUavBinding, + int(batch.startBinding + count) - 1); + } } } @@ -3529,11 +3601,15 @@ static pD3DCompile resolveD3DCompile() static QByteArray compileHlslShaderSource(const QShader &shader, QShader::Variant shaderVariant, QString *error, QShaderKey *usedShaderKey) { - QShaderCode dxbc = shader.shader({ QShader::DxbcShader, 50, shaderVariant }); - if (!dxbc.shader().isEmpty()) + QShaderKey key = { QShader::DxbcShader, 50, shaderVariant }; + QShaderCode dxbc = shader.shader(key); + if (!dxbc.shader().isEmpty()) { + if (usedShaderKey) + *usedShaderKey = key; return dxbc.shader(); + } - const QShaderKey key = { QShader::HlslShader, 50, shaderVariant }; + key = { QShader::HlslShader, 50, shaderVariant }; QShaderCode hlslSource = shader.shader(key); if (hlslSource.shader().isEmpty()) { qWarning() << "No HLSL (shader model 5.0) code found in baked shader" << shader; diff --git a/src/gui/rhi/qrhid3d11_p_p.h b/src/gui/rhi/qrhid3d11_p_p.h index f749b612b5..33412b8011 100644 --- a/src/gui/rhi/qrhid3d11_p_p.h +++ b/src/gui/rhi/qrhid3d11_p_p.h @@ -210,10 +210,13 @@ struct QD3D11ShaderResourceBindings : public QRhiShaderResourceBindings uint generation; }; struct BoundSampledTextureData { - quint64 texId; - uint texGeneration; - quint64 samplerId; - uint samplerGeneration; + int count; + struct { + quint64 texId; + uint texGeneration; + quint64 samplerId; + uint samplerGeneration; + } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE]; }; struct BoundStorageImageData { quint64 id; diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp index 4a442bc582..ea9bce08e4 100644 --- a/src/gui/rhi/qrhigles2.cpp +++ b/src/gui/rhi/qrhigles2.cpp @@ -917,10 +917,12 @@ void QRhiGles2::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind hasDynamicOffsetInSrb = true; break; case QRhiShaderResourceBinding::SampledTexture: - trackedRegisterTexture(&passResTracker, - QRHI_RES(QGles2Texture, b->u.stex.tex), - QRhiPassResourceTracker::TexSample, - QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage)); + for (int elem = 0; elem < b->u.stex.count; ++elem) { + trackedRegisterTexture(&passResTracker, + QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex), + QRhiPassResourceTracker::TexSample, + QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage)); + } break; case QRhiShaderResourceBinding::ImageLoad: case QRhiShaderResourceBinding::ImageStore: @@ -2572,36 +2574,37 @@ void QRhiGles2::bindShaderResources(QRhiGraphicsPipeline *maybeGraphicsPs, QRhiC break; case QRhiShaderResourceBinding::SampledTexture: { - QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.tex); - QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.sampler); QVector<QGles2SamplerDescription> &samplers(maybeGraphicsPs ? QRHI_RES(QGles2GraphicsPipeline, maybeGraphicsPs)->samplers : QRHI_RES(QGles2ComputePipeline, maybeComputePs)->samplers); - - for (QGles2SamplerDescription &sampler : samplers) { - if (sampler.binding == b->binding) { - f->glActiveTexture(GL_TEXTURE0 + uint(texUnit)); - f->glBindTexture(texD->target, texD->texture); - - if (texD->samplerState != samplerD->d) { - f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, GLint(samplerD->d.glminfilter)); - f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter)); - f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps)); - f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt)); - // 3D textures not supported by GLES 2.0 or by us atm... - //f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr); - if (caps.textureCompareMode) { - if (samplerD->d.gltexcomparefunc != GL_NEVER) { - f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, GLint(samplerD->d.gltexcomparefunc)); - } else { - f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE); + for (int elem = 0; elem < b->u.stex.count; ++elem) { + QGles2Texture *texD = QRHI_RES(QGles2Texture, b->u.stex.texSamplers[elem].tex); + QGles2Sampler *samplerD = QRHI_RES(QGles2Sampler, b->u.stex.texSamplers[elem].sampler); + for (QGles2SamplerDescription &sampler : samplers) { + if (sampler.binding == b->binding) { + f->glActiveTexture(GL_TEXTURE0 + uint(texUnit)); + f->glBindTexture(texD->target, texD->texture); + + if (texD->samplerState != samplerD->d) { + f->glTexParameteri(texD->target, GL_TEXTURE_MIN_FILTER, GLint(samplerD->d.glminfilter)); + f->glTexParameteri(texD->target, GL_TEXTURE_MAG_FILTER, GLint(samplerD->d.glmagfilter)); + f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_S, GLint(samplerD->d.glwraps)); + f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_T, GLint(samplerD->d.glwrapt)); + // 3D textures not supported by GLES 2.0 or by us atm... + //f->glTexParameteri(texD->target, GL_TEXTURE_WRAP_R, samplerD->d.glwrapr); + if (caps.textureCompareMode) { + if (samplerD->d.gltexcomparefunc != GL_NEVER) { + f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_FUNC, GLint(samplerD->d.gltexcomparefunc)); + } else { + f->glTexParameteri(texD->target, GL_TEXTURE_COMPARE_MODE, GL_NONE); + } } + texD->samplerState = samplerD->d; } - texD->samplerState = samplerD->d; - } - f->glUniform1i(sampler.glslLocation, texUnit); - ++texUnit; + f->glUniform1i(sampler.glslLocation + elem, texUnit); + ++texUnit; + } } } } diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 314c58b0b7..0806c8a052 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -748,30 +748,33 @@ void QRhiMetal::enqueueShaderResourceBindings(QMetalShaderResourceBindings *srbD break; case QRhiShaderResourceBinding::SampledTexture: { - QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex); - QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler); - if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { - const int nativeBindingTexture = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture); - const int nativeBindingSampler = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler); - if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { - res[VERTEX].textures.append({ nativeBindingTexture, texD->d->tex }); - res[VERTEX].samplers.append({ nativeBindingSampler, samplerD->d->samplerState }); + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + for (int elem = 0; elem < data->count; ++elem) { + QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.texSamplers[elem].tex); + QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.texSamplers[elem].sampler); + if (b->stage.testFlag(QRhiShaderResourceBinding::VertexStage)) { + const int nativeBindingTexture = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Texture); + const int nativeBindingSampler = mapBinding(b->binding, VERTEX, nativeResourceBindingMaps, BindingType::Sampler); + if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { + res[VERTEX].textures.append({ nativeBindingTexture + elem, texD->d->tex }); + res[VERTEX].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState }); + } } - } - if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { - const int nativeBindingTexture = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture); - const int nativeBindingSampler = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler); - if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { - res[FRAGMENT].textures.append({ nativeBindingTexture, texD->d->tex }); - res[FRAGMENT].samplers.append({ nativeBindingSampler, samplerD->d->samplerState }); + if (b->stage.testFlag(QRhiShaderResourceBinding::FragmentStage)) { + const int nativeBindingTexture = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Texture); + const int nativeBindingSampler = mapBinding(b->binding, FRAGMENT, nativeResourceBindingMaps, BindingType::Sampler); + if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { + res[FRAGMENT].textures.append({ nativeBindingTexture + elem, texD->d->tex }); + res[FRAGMENT].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState }); + } } - } - if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { - const int nativeBindingTexture = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture); - const int nativeBindingSampler = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler); - if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { - res[COMPUTE].textures.append({ nativeBindingTexture, texD->d->tex }); - res[COMPUTE].samplers.append({ nativeBindingSampler, samplerD->d->samplerState }); + if (b->stage.testFlag(QRhiShaderResourceBinding::ComputeStage)) { + const int nativeBindingTexture = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Texture); + const int nativeBindingSampler = mapBinding(b->binding, COMPUTE, nativeResourceBindingMaps, BindingType::Sampler); + if (nativeBindingTexture >= 0 && nativeBindingSampler >= 0) { + res[COMPUTE].textures.append({ nativeBindingTexture + elem, texD->d->tex }); + res[COMPUTE].samplers.append({ nativeBindingSampler + elem, samplerD->d->samplerState }); + } } } } @@ -1020,21 +1023,28 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind break; case QRhiShaderResourceBinding::SampledTexture: { - QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex); - QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler); - if (texD->generation != bd.stex.texGeneration - || texD->m_id != bd.stex.texId - || samplerD->generation != bd.stex.samplerGeneration - || samplerD->m_id != bd.stex.samplerId) - { + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + if (bd.stex.count != data->count) { + bd.stex.count = data->count; resNeedsRebind = true; - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; } - texD->lastActiveFrameSlot = currentFrameSlot; - samplerD->lastActiveFrameSlot = currentFrameSlot; + for (int elem = 0; elem < data->count; ++elem) { + QMetalTexture *texD = QRHI_RES(QMetalTexture, data->texSamplers[elem].tex); + QMetalSampler *samplerD = QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler); + if (texD->generation != bd.stex.d[elem].texGeneration + || texD->m_id != bd.stex.d[elem].texId + || samplerD->generation != bd.stex.d[elem].samplerGeneration + || samplerD->m_id != bd.stex.d[elem].samplerId) + { + resNeedsRebind = true; + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + } + texD->lastActiveFrameSlot = currentFrameSlot; + samplerD->lastActiveFrameSlot = currentFrameSlot; + } } break; case QRhiShaderResourceBinding::ImageLoad: @@ -2981,12 +2991,16 @@ bool QMetalShaderResourceBindings::build() break; case QRhiShaderResourceBinding::SampledTexture: { - QMetalTexture *texD = QRHI_RES(QMetalTexture, b->u.stex.tex); - QMetalSampler *samplerD = QRHI_RES(QMetalSampler, b->u.stex.sampler); - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + bd.stex.count = data->count; + for (int elem = 0; elem < data->count; ++elem) { + QMetalTexture *texD = QRHI_RES(QMetalTexture, data->texSamplers[elem].tex); + QMetalSampler *samplerD = QRHI_RES(QMetalSampler, data->texSamplers[elem].sampler); + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + } } break; case QRhiShaderResourceBinding::ImageLoad: @@ -3241,8 +3255,12 @@ static inline MTLCullMode toMetalCullMode(QRhiGraphicsPipeline::CullMode c) id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Variant shaderVariant, QString *error, QByteArray *entryPoint, QShaderKey *activeKey) { - QShaderKey key = { QShader::MetalLibShader, 12, shaderVariant }; + QShaderKey key = { QShader::MetalLibShader, 20, shaderVariant }; QShaderCode mtllib = shader.shader(key); + if (mtllib.shader().isEmpty()) { + key.setSourceVersion(12); + mtllib = shader.shader(key); + } if (!mtllib.shader().isEmpty()) { dispatch_data_t data = dispatch_data_create(mtllib.shader().constData(), size_t(mtllib.shader().size()), @@ -3261,16 +3279,20 @@ id<MTLLibrary> QRhiMetalData::createMetalLib(const QShader &shader, QShader::Var } } - key = { QShader::MslShader, 12, shaderVariant }; + key = { QShader::MslShader, 20, shaderVariant }; QShaderCode mslSource = shader.shader(key); if (mslSource.shader().isEmpty()) { - qWarning() << "No MSL 1.2 code found in baked shader" << shader; + key.setSourceVersion(12); + mslSource = shader.shader(key); + } + if (mslSource.shader().isEmpty()) { + qWarning() << "No MSL 2.0 or 1.2 code found in baked shader" << shader; return nil; } NSString *src = [NSString stringWithUTF8String: mslSource.shader().constData()]; MTLCompileOptions *opts = [[MTLCompileOptions alloc] init]; - opts.languageVersion = MTLLanguageVersion1_2; + opts.languageVersion = key.sourceVersion() == 20 ? MTLLanguageVersion2_0 : MTLLanguageVersion1_2; NSError *err = nil; id<MTLLibrary> lib = [dev newLibraryWithSource: src options: opts error: &err]; [opts release]; diff --git a/src/gui/rhi/qrhimetal_p_p.h b/src/gui/rhi/qrhimetal_p_p.h index a5af5611a6..cb4b777d88 100644 --- a/src/gui/rhi/qrhimetal_p_p.h +++ b/src/gui/rhi/qrhimetal_p_p.h @@ -197,10 +197,13 @@ struct QMetalShaderResourceBindings : public QRhiShaderResourceBindings uint generation; }; struct BoundSampledTextureData { - quint64 texId; - uint texGeneration; - quint64 samplerId; - uint samplerGeneration; + int count; + struct { + quint64 texId; + uint texGeneration; + quint64 samplerId; + uint samplerGeneration; + } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE]; }; struct BoundStorageImageData { quint64 id; diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index a92c3e14e9..26c153afff 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -345,6 +345,15 @@ static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTyp return true; } + // In certain cases allocateDescriptorSet() will attempt to allocate from a + // pool that does not have enough descriptors of a certain type. This makes + // the validation layer shout. However, this is not an error since we will + // then move on to another pool. If there is a real error, a qWarning + // message is shown by allocateDescriptorSet(), so the validation warning + // does not have any value and is just noise. + if (strstr(pMessage, "VUID-VkDescriptorSetAllocateInfo-descriptorPool-00307")) + return true; + return false; } @@ -2487,7 +2496,8 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb); QVarLengthArray<VkDescriptorBufferInfo, 8> bufferInfos; - QVarLengthArray<VkDescriptorImageInfo, 8> imageInfos; + using ArrayOfImageDesc = QVarLengthArray<VkDescriptorImageInfo, 8>; + QVarLengthArray<ArrayOfImageDesc, 8> imageInfos; QVarLengthArray<VkWriteDescriptorSet, 12> writeInfos; QVarLengthArray<QPair<int, int>, 12> infoIndices; @@ -2530,17 +2540,22 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i break; case QRhiShaderResourceBinding::SampledTexture: { - QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex); - QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler); + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + writeInfo.descriptorCount = data->count; // arrays of combined image samplers are supported writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; - VkDescriptorImageInfo imageInfo; - imageInfo.sampler = samplerD->sampler; - imageInfo.imageView = texD->imageView; - imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + ArrayOfImageDesc imageInfo(data->count); + for (int elem = 0; elem < data->count; ++elem) { + QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex); + QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler); + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + imageInfo[elem].sampler = samplerD->sampler; + imageInfo[elem].imageView = texD->imageView; + imageInfo[elem].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + bd.stex.count = data->count; imageInfoIndex = imageInfos.count(); imageInfos.append(imageInfo); } @@ -2555,10 +2570,10 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; bd.simage.id = texD->m_id; bd.simage.generation = texD->generation; - VkDescriptorImageInfo imageInfo; - imageInfo.sampler = VK_NULL_HANDLE; - imageInfo.imageView = view; - imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; + ArrayOfImageDesc imageInfo(1); + imageInfo[0].sampler = VK_NULL_HANDLE; + imageInfo[0].imageView = view; + imageInfo[0].imageLayout = VK_IMAGE_LAYOUT_GENERAL; imageInfoIndex = imageInfos.count(); imageInfos.append(imageInfo); } @@ -2596,7 +2611,7 @@ void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, i if (bufferInfoIndex >= 0) writeInfos[i].pBufferInfo = &bufferInfos[bufferInfoIndex]; else if (imageInfoIndex >= 0) - writeInfos[i].pImageInfo = &imageInfos[imageInfoIndex]; + writeInfos[i].pImageInfo = imageInfos[imageInfoIndex].constData(); } df->vkUpdateDescriptorSets(dev, uint32_t(writeInfos.count()), writeInfos.constData(), 0, nullptr); @@ -4210,24 +4225,30 @@ void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBin break; case QRhiShaderResourceBinding::SampledTexture: { - QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex); - QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler); - texD->lastActiveFrameSlot = currentFrameSlot; - samplerD->lastActiveFrameSlot = currentFrameSlot; - trackedRegisterTexture(&passResTracker, texD, - QRhiPassResourceTracker::TexSample, - QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage)); - - if (texD->generation != bd.stex.texGeneration - || texD->m_id != bd.stex.texId - || samplerD->generation != bd.stex.samplerGeneration - || samplerD->m_id != bd.stex.samplerId) - { + const QRhiShaderResourceBinding::Data::SampledTextureData *data = &b->u.stex; + if (bd.stex.count != data->count) { + bd.stex.count = data->count; rewriteDescSet = true; - bd.stex.texId = texD->m_id; - bd.stex.texGeneration = texD->generation; - bd.stex.samplerId = samplerD->m_id; - bd.stex.samplerGeneration = samplerD->generation; + } + for (int elem = 0; elem < data->count; ++elem) { + QVkTexture *texD = QRHI_RES(QVkTexture, data->texSamplers[elem].tex); + QVkSampler *samplerD = QRHI_RES(QVkSampler, data->texSamplers[elem].sampler); + texD->lastActiveFrameSlot = currentFrameSlot; + samplerD->lastActiveFrameSlot = currentFrameSlot; + trackedRegisterTexture(&passResTracker, texD, + QRhiPassResourceTracker::TexSample, + QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage)); + if (texD->generation != bd.stex.d[elem].texGeneration + || texD->m_id != bd.stex.d[elem].texId + || samplerD->generation != bd.stex.d[elem].samplerGeneration + || samplerD->m_id != bd.stex.d[elem].samplerId) + { + rewriteDescSet = true; + bd.stex.d[elem].texId = texD->m_id; + bd.stex.d[elem].texGeneration = texD->generation; + bd.stex.d[elem].samplerId = samplerD->m_id; + bd.stex.d[elem].samplerGeneration = samplerD->generation; + } } } break; @@ -6065,7 +6086,10 @@ bool QVkShaderResourceBindings::build() memset(&vkbinding, 0, sizeof(vkbinding)); vkbinding.binding = uint32_t(b->binding); vkbinding.descriptorType = toVkDescriptorType(b); - vkbinding.descriptorCount = 1; // no array support yet + if (b->type == QRhiShaderResourceBinding::SampledTexture) + vkbinding.descriptorCount = b->u.stex.count; + else + vkbinding.descriptorCount = 1; vkbinding.stageFlags = toVkShaderStageFlags(b->stage); vkbindings.append(vkbinding); } diff --git a/src/gui/rhi/qrhivulkan_p_p.h b/src/gui/rhi/qrhivulkan_p_p.h index fd65417e75..62516e268d 100644 --- a/src/gui/rhi/qrhivulkan_p_p.h +++ b/src/gui/rhi/qrhivulkan_p_p.h @@ -254,10 +254,13 @@ struct QVkShaderResourceBindings : public QRhiShaderResourceBindings uint generation; }; struct BoundSampledTextureData { - quint64 texId; - uint texGeneration; - quint64 samplerId; - uint samplerGeneration; + int count; + struct { + quint64 texId; + uint texGeneration; + quint64 samplerId; + uint samplerGeneration; + } d[QRhiShaderResourceBinding::Data::MAX_TEX_SAMPLER_ARRAY_SIZE]; }; struct BoundStorageImageData { quint64 id; diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp index 884525bd76..b4b60cbaaf 100644 --- a/src/gui/text/qrawfont.cpp +++ b/src/gui/text/qrawfont.cpp @@ -51,6 +51,7 @@ #include <QtCore/qendian.h> #include <QtCore/qfile.h> +#include <QtGui/qpainterpath.h> QT_BEGIN_NAMESPACE diff --git a/src/gui/vulkan/qvulkanwindow.cpp b/src/gui/vulkan/qvulkanwindow.cpp index ee49cf0999..cb89b0b1e6 100644 --- a/src/gui/vulkan/qvulkanwindow.cpp +++ b/src/gui/vulkan/qvulkanwindow.cpp @@ -1602,7 +1602,7 @@ bool QVulkanWindow::event(QEvent *e) \since 5.15 */ -void QVulkanWindow::setQueueCreateInfoModifier(QueueCreateInfoModifier modifier) +void QVulkanWindow::setQueueCreateInfoModifier(const QueueCreateInfoModifier &modifier) { Q_D(QVulkanWindow); d->queueCreateInfoModifier = modifier; diff --git a/src/gui/vulkan/qvulkanwindow.h b/src/gui/vulkan/qvulkanwindow.h index 511b9501bd..a2d1c9995c 100644 --- a/src/gui/vulkan/qvulkanwindow.h +++ b/src/gui/vulkan/qvulkanwindow.h @@ -106,7 +106,7 @@ public: typedef std::function<void(const VkQueueFamilyProperties *, uint32_t, QVector<VkDeviceQueueCreateInfo> &)> QueueCreateInfoModifier; - void setQueueCreateInfoModifier(QueueCreateInfoModifier modifier); + void setQueueCreateInfoModifier(const QueueCreateInfoModifier &modifier); bool isValid() const; |