Add support for painting at integer DPR with downscale
Enable by setting QT_WIDGETS_HIGHDPI_DOWNSCALE=1 and QT_WIDGETS_RHI=1. This will make the backing store and painter operate at the next highest integer DPR in cases where QWindow::devicePixelRatio() returns a fractional value. The backing store image will then be downscaled to the target DPR at flush time, using the RHI flush pipeline. [ChangeLog][QWidgets] Added experimental support for always painting at an integer device pixel ratio (rounding the DPR up if necessary), followed by a downscale to the target DPR.Enable by setting QT_WIDGETS_HIGHDPI_DOWNSCALE=1 and QT_WIDGETS_RHI=1. Task-number: QTBUG-86344 Change-Id: Id5b834a0e3499818b0b656161f5e0c38a6caa340 Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io> (cherry picked from commit 79bead6c3b507331614dcc3c789e18438bc10395) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
b623835dd9
commit
df1beb422a
@ -25,11 +25,32 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the DPR for the backing store. This is the DPR for the QWindow,
|
||||||
|
// possibly rounded up to the nearest integer.
|
||||||
|
qreal backingStoreDevicePixelRatio() const
|
||||||
|
{
|
||||||
|
// Note: keep in sync with QWidget::metric()!
|
||||||
|
qreal windowDpr = window->devicePixelRatio();
|
||||||
|
return downscale ? std::ceil(windowDpr) : windowDpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the factor used for converting from device independent to native
|
||||||
|
// backing store sizes. Normally this is just the gui scale factor, however
|
||||||
|
// if the backing store rounds the DPR up to the nearest integer then we also
|
||||||
|
// need to account for the factor introduced by that rounding.
|
||||||
|
qreal deviceIndependentToNativeFactor() const
|
||||||
|
{
|
||||||
|
const qreal roundingFactor = backingStoreDevicePixelRatio() / window->devicePixelRatio();
|
||||||
|
const qreal guiFactor = QHighDpiScaling::factor(window);
|
||||||
|
return roundingFactor * guiFactor;
|
||||||
|
}
|
||||||
|
|
||||||
QWindow *window;
|
QWindow *window;
|
||||||
QPlatformBackingStore *platformBackingStore = nullptr;
|
QPlatformBackingStore *platformBackingStore = nullptr;
|
||||||
QScopedPointer<QImage> highDpiBackingstore;
|
QScopedPointer<QImage> highDpiBackingstore;
|
||||||
QRegion staticContents;
|
QRegion staticContents;
|
||||||
QSize size;
|
QSize size;
|
||||||
|
bool downscale = qEnvironmentVariableIntValue("QT_WIDGETS_HIGHDPI_DOWNSCALE") > 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -94,12 +115,14 @@ QWindow* QBackingStore::window() const
|
|||||||
|
|
||||||
void QBackingStore::beginPaint(const QRegion ®ion)
|
void QBackingStore::beginPaint(const QRegion ®ion)
|
||||||
{
|
{
|
||||||
|
const qreal dpr = d_ptr->backingStoreDevicePixelRatio();
|
||||||
|
|
||||||
if (d_ptr->highDpiBackingstore &&
|
if (d_ptr->highDpiBackingstore &&
|
||||||
d_ptr->highDpiBackingstore->devicePixelRatio() != d_ptr->window->devicePixelRatio())
|
d_ptr->highDpiBackingstore->devicePixelRatio() != dpr)
|
||||||
resize(size());
|
resize(size());
|
||||||
|
|
||||||
QPlatformBackingStore *platformBackingStore = handle();
|
QPlatformBackingStore *platformBackingStore = handle();
|
||||||
platformBackingStore->beginPaint(QHighDpi::toNativeLocalRegion(region, d_ptr->window));
|
platformBackingStore->beginPaint(QHighDpi::scale(region, d_ptr->deviceIndependentToNativeFactor()));
|
||||||
|
|
||||||
// When QtGui is applying a high-dpi scale factor the backing store
|
// When QtGui is applying a high-dpi scale factor the backing store
|
||||||
// creates a "large" backing store image. This image needs to be
|
// creates a "large" backing store image. This image needs to be
|
||||||
@ -118,8 +141,7 @@ void QBackingStore::beginPaint(const QRegion ®ion)
|
|||||||
d_ptr->highDpiBackingstore.reset(
|
d_ptr->highDpiBackingstore.reset(
|
||||||
new QImage(source->bits(), source->width(), source->height(), source->bytesPerLine(), source->format()));
|
new QImage(source->bits(), source->width(), source->height(), source->bytesPerLine(), source->format()));
|
||||||
|
|
||||||
qreal targetDevicePixelRatio = d_ptr->window->devicePixelRatio();
|
d_ptr->highDpiBackingstore->setDevicePixelRatio(dpr);
|
||||||
d_ptr->highDpiBackingstore->setDevicePixelRatio(targetDevicePixelRatio);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,13 +206,15 @@ void QBackingStore::flush(const QRegion ®ion, QWindow *window, const QPoint &
|
|||||||
|
|
||||||
Q_ASSERT(window == topLevelWindow || topLevelWindow->isAncestorOf(window, QWindow::ExcludeTransients));
|
Q_ASSERT(window == topLevelWindow || topLevelWindow->isAncestorOf(window, QWindow::ExcludeTransients));
|
||||||
|
|
||||||
QRegion nativeRegion = QHighDpi::toNativeLocalRegion(region, window);
|
const qreal toNativeFactor = d_ptr->deviceIndependentToNativeFactor();
|
||||||
|
|
||||||
|
QRegion nativeRegion = QHighDpi::scale(region, toNativeFactor);
|
||||||
QPoint nativeOffset;
|
QPoint nativeOffset;
|
||||||
if (!offset.isNull()) {
|
if (!offset.isNull()) {
|
||||||
nativeOffset = QHighDpi::toNativeLocalPosition(offset, window);
|
nativeOffset = QHighDpi::scale(offset, toNativeFactor);
|
||||||
// Under fractional DPR, rounding of region and offset may accumulate to an off-by-one
|
// Under fractional DPR, rounding of region and offset may accumulate to an off-by-one
|
||||||
QPoint topLeft = region.boundingRect().topLeft() + offset;
|
QPoint topLeft = region.boundingRect().topLeft() + offset;
|
||||||
QPoint nativeTopLeft = QHighDpi::toNativeLocalPosition(topLeft, window);
|
QPoint nativeTopLeft = QHighDpi::scale(topLeft, toNativeFactor);
|
||||||
QPoint diff = nativeTopLeft - (nativeRegion.boundingRect().topLeft() + nativeOffset);
|
QPoint diff = nativeTopLeft - (nativeRegion.boundingRect().topLeft() + nativeOffset);
|
||||||
Q_ASSERT(qMax(qAbs(diff.x()), qAbs(diff.y())) <= 1);
|
Q_ASSERT(qMax(qAbs(diff.x()), qAbs(diff.y())) <= 1);
|
||||||
nativeRegion.translate(diff);
|
nativeRegion.translate(diff);
|
||||||
@ -206,7 +230,7 @@ void QBackingStore::flush(const QRegion ®ion, QWindow *window, const QPoint &
|
|||||||
void QBackingStore::resize(const QSize &size)
|
void QBackingStore::resize(const QSize &size)
|
||||||
{
|
{
|
||||||
d_ptr->size = size;
|
d_ptr->size = size;
|
||||||
handle()->resize(QHighDpi::toNativePixels(size, d_ptr->window), d_ptr->staticContents);
|
handle()->resize(QHighDpi::scale(size, d_ptr->deviceIndependentToNativeFactor()), d_ptr->staticContents);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -228,13 +252,13 @@ bool QBackingStore::scroll(const QRegion &area, int dx, int dy)
|
|||||||
// Disable scrolling for non-integer scroll deltas. For this case
|
// Disable scrolling for non-integer scroll deltas. For this case
|
||||||
// the existing rendered pixels can't be re-used, and we return
|
// the existing rendered pixels can't be re-used, and we return
|
||||||
// false to signal that a repaint is needed.
|
// false to signal that a repaint is needed.
|
||||||
const qreal nativeDx = QHighDpi::toNativePixels(qreal(dx), d_ptr->window);
|
const qreal toNativeFactor = d_ptr->deviceIndependentToNativeFactor();
|
||||||
const qreal nativeDy = QHighDpi::toNativePixels(qreal(dy), d_ptr->window);
|
const qreal nativeDx = QHighDpi::scale(qreal(dx), toNativeFactor);
|
||||||
|
const qreal nativeDy = QHighDpi::scale(qreal(dy), toNativeFactor);
|
||||||
if (qFloor(nativeDx) != nativeDx || qFloor(nativeDy) != nativeDy)
|
if (qFloor(nativeDx) != nativeDx || qFloor(nativeDy) != nativeDy)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return handle()->scroll(QHighDpi::toNativeLocalRegion(area, d_ptr->window),
|
return handle()->scroll(QHighDpi::scale(area, toNativeFactor), nativeDx, nativeDy);
|
||||||
nativeDx, nativeDy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -408,7 +408,7 @@ void QBackingStoreDefaultCompositor::ensureResources(QRhiSwapChain *swapchain, Q
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!m_sampler) {
|
if (!m_sampler) {
|
||||||
m_sampler = m_rhi->newSampler(QRhiSampler::Nearest, QRhiSampler::Nearest, QRhiSampler::None,
|
m_sampler = m_rhi->newSampler(QRhiSampler::Linear, QRhiSampler::Linear, QRhiSampler::None,
|
||||||
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
|
QRhiSampler::ClampToEdge, QRhiSampler::ClampToEdge);
|
||||||
if (!m_sampler->create())
|
if (!m_sampler->create())
|
||||||
qWarning("QBackingStoreDefaultCompositor: Failed to create sampler");
|
qWarning("QBackingStoreDefaultCompositor: Failed to create sampler");
|
||||||
@ -506,7 +506,8 @@ QPlatformBackingStore::FlushResult QBackingStoreDefaultCompositor::flush(QPlatfo
|
|||||||
if (m_texture) {
|
if (m_texture) {
|
||||||
// The backingstore is for the entire tlw.
|
// The backingstore is for the entire tlw.
|
||||||
// In case of native children offset tells the position relative to the tlw.
|
// In case of native children offset tells the position relative to the tlw.
|
||||||
const QRect srcRect = toBottomLeftRect(deviceWindowRect.translated(deviceWindowOffset), m_texture->pixelSize().height());
|
const QRect textureRect = QRect(QPoint(), m_texture->pixelSize());
|
||||||
|
const QRect srcRect = toBottomLeftRect(textureRect.translated(deviceWindowOffset), m_texture->pixelSize().height());
|
||||||
const QMatrix3x3 source = sourceTransform(srcRect, m_texture->pixelSize(), origin);
|
const QMatrix3x3 source = sourceTransform(srcRect, m_texture->pixelSize(), origin);
|
||||||
QMatrix4x4 target; // identity
|
QMatrix4x4 target; // identity
|
||||||
if (invertTargetY)
|
if (invertTargetY)
|
||||||
|
@ -12698,6 +12698,17 @@ int QWidget::metric(PaintDeviceMetric m) const
|
|||||||
return QPaintDevice::metric(m);
|
return QPaintDevice::metric(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto resolveDevicePixelRatio = [this, screen]() -> qreal {
|
||||||
|
|
||||||
|
// Note: keep in sync with QBackingStorePrivate::backingStoreDevicePixelRatio()!
|
||||||
|
static bool downscale = qEnvironmentVariableIntValue("QT_WIDGETS_HIGHDPI_DOWNSCALE") > 0;
|
||||||
|
QWindow *window = this->window()->windowHandle();
|
||||||
|
if (downscale && window)
|
||||||
|
return std::ceil(window->devicePixelRatio());
|
||||||
|
|
||||||
|
return screen->devicePixelRatio();
|
||||||
|
};
|
||||||
|
|
||||||
switch (m) {
|
switch (m) {
|
||||||
case PdmWidth:
|
case PdmWidth:
|
||||||
return data->crect.width();
|
return data->crect.width();
|
||||||
@ -12726,9 +12737,9 @@ int QWidget::metric(PaintDeviceMetric m) const
|
|||||||
case PdmPhysicalDpiY:
|
case PdmPhysicalDpiY:
|
||||||
return qRound(screen->physicalDotsPerInchY());
|
return qRound(screen->physicalDotsPerInchY());
|
||||||
case PdmDevicePixelRatio:
|
case PdmDevicePixelRatio:
|
||||||
return screen->devicePixelRatio();
|
return resolveDevicePixelRatio();
|
||||||
case PdmDevicePixelRatioScaled:
|
case PdmDevicePixelRatioScaled:
|
||||||
return QPaintDevice::devicePixelRatioFScale() * screen->devicePixelRatio();
|
return QPaintDevice::devicePixelRatioFScale() * resolveDevicePixelRatio();
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ bool g_qtScaleFactor = false;
|
|||||||
bool g_qtUsePhysicalDpi = false;
|
bool g_qtUsePhysicalDpi = false;
|
||||||
bool g_qtFontDpi = false;
|
bool g_qtFontDpi = false;
|
||||||
bool g_qtScaleFactorRoundingPolicy = false;
|
bool g_qtScaleFactorRoundingPolicy = false;
|
||||||
|
bool g_qtHighDpiDownscale = false;
|
||||||
bool g_displayEvents = false;
|
bool g_displayEvents = false;
|
||||||
|
|
||||||
|
|
||||||
@ -136,7 +137,7 @@ public:
|
|||||||
if (g_displayEvents)
|
if (g_displayEvents)
|
||||||
layout->addWidget(eventsLabel);
|
layout->addWidget(eventsLabel);
|
||||||
|
|
||||||
bool activeEnvironment = g_qtScaleFactor || g_qtUsePhysicalDpi || g_qtFontDpi || g_qtScaleFactorRoundingPolicy;
|
bool activeEnvironment = g_qtScaleFactor || g_qtUsePhysicalDpi || g_qtFontDpi || g_qtScaleFactorRoundingPolicy || g_qtHighDpiDownscale;
|
||||||
if (activeEnvironment) {
|
if (activeEnvironment) {
|
||||||
layout->addWidget(new QLabel("Active Environment:"));
|
layout->addWidget(new QLabel("Active Environment:"));
|
||||||
if (g_qtScaleFactor) {
|
if (g_qtScaleFactor) {
|
||||||
@ -155,7 +156,10 @@ public:
|
|||||||
QString text = QString("QT_SCALE_FACTOR_ROUNDING_POLICY=") + qgetenv("QT_SCALE_FACTOR_ROUNDING_POLICY");
|
QString text = QString("QT_SCALE_FACTOR_ROUNDING_POLICY=") + qgetenv("QT_SCALE_FACTOR_ROUNDING_POLICY");
|
||||||
layout->addWidget(new QLabel(text));
|
layout->addWidget(new QLabel(text));
|
||||||
}
|
}
|
||||||
|
if (g_qtHighDpiDownscale) {
|
||||||
|
QString text = QString("QT_WIDGETS_HIGHDPI_DOWNSCALE=") + qgetenv("QT_WIDGETS_HIGHDPI_DOWNSCALE");
|
||||||
|
layout->addWidget(new QLabel(text));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto updateValues = [=]() {
|
auto updateValues = [=]() {
|
||||||
@ -244,6 +248,7 @@ int main(int argc, char **argv) {
|
|||||||
g_qtUsePhysicalDpi = qgetenv("QT_USE_PHYSICAL_DPI") == QByteArray("1");
|
g_qtUsePhysicalDpi = qgetenv("QT_USE_PHYSICAL_DPI") == QByteArray("1");
|
||||||
g_qtFontDpi = qEnvironmentVariableIsSet("QT_FONT_DPI");
|
g_qtFontDpi = qEnvironmentVariableIsSet("QT_FONT_DPI");
|
||||||
g_qtScaleFactorRoundingPolicy = qEnvironmentVariableIsSet("QT_SCALE_FACTOR_ROUNDING_POLICY");
|
g_qtScaleFactorRoundingPolicy = qEnvironmentVariableIsSet("QT_SCALE_FACTOR_ROUNDING_POLICY");
|
||||||
|
g_qtHighDpiDownscale = qEnvironmentVariableIsSet("QT_WIDGETS_HIGHDPI_DOWNSCALE");
|
||||||
|
|
||||||
QApplication app(argc, argv);
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user