summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorten Johan Sørvig <morten.sorvig@qt.io>2019-05-08 09:23:52 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2022-01-06 01:12:07 +0000
commit7cfd6cb61cefcdc261b4e96b4ed5541eb56278df (patch)
treeaab13c10ab8bc8d75b182639ad2ec17f929c40b3
parent4b8938f3be7e73ca9badf427d465620a9988efea (diff)
wasm: always use requestAnimationFrame for updates
The compositor was posting update events and flushing/redrawing using a zero-timer. Change this to use the request_animation_frame API from Emscripten, which makes sure we flush window content at the next native paint event. This has the additional benefit that hidden canvases (e.g on hidden tabs) won’t get frame events, and then stop painting. We support both well-behaved QWindows, where the window calls requestUpate() and then paints/flushes on the following deliverUpdateRequest(), and also less well behaved windows which paints at any point during event processing. Change-Id: I747d6f7ace86ceddaa18ab86b6a0ee833f98991b Reviewed-by: Lorn Potter <lorn.potter@gmail.com> (cherry picked from commit eb62b6ac02b86f11bc6bcbe1f37df003d2a46d02) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/plugins/platforms/wasm/qwasmbackingstore.cpp2
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.cpp148
-rw-r--r--src/plugins/platforms/wasm/qwasmcompositor.h19
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.cpp2
-rw-r--r--src/plugins/platforms/wasm/qwasmwindow.cpp10
5 files changed, 116 insertions, 65 deletions
diff --git a/src/plugins/platforms/wasm/qwasmbackingstore.cpp b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
index 3d667ccf97..989c2a1b90 100644
--- a/src/plugins/platforms/wasm/qwasmbackingstore.cpp
+++ b/src/plugins/platforms/wasm/qwasmbackingstore.cpp
@@ -90,7 +90,7 @@ void QWasmBackingStore::flush(QWindow *window, const QRegion &region, const QPoi
Q_UNUSED(offset);
m_dirty |= region;
- m_compositor->requestRedraw();
+ m_compositor->handleBackingStoreFlush();
}
void QWasmBackingStore::updateTexture()
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.cpp b/src/plugins/platforms/wasm/qwasmcompositor.cpp
index 063144d71d..aa498c8238 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.cpp
+++ b/src/plugins/platforms/wasm/qwasmcompositor.cpp
@@ -28,6 +28,7 @@
****************************************************************************/
#include "qwasmcompositor.h"
+#include "qwasmeventdispatcher.h"
#include "qwasmwindow.h"
#include "qwasmstylepixmaps_p.h"
@@ -69,6 +70,8 @@ QWasmCompositor::QWasmCompositor(QWasmScreen *screen)
QWasmCompositor::~QWasmCompositor()
{
+ if (m_requestAnimationFrameId != -1)
+ emscripten_cancel_animation_frame(m_requestAnimationFrameId);
destroy();
}
@@ -148,7 +151,7 @@ void QWasmCompositor::setVisible(QWasmWindow *window, bool visible)
else
m_globalDamage = compositedWindow.window->geometry(); // repaint previously covered area.
- requestRedraw();
+ requestUpdateWindow(window, QWasmCompositor::ExposeEventDelivery);
}
void QWasmCompositor::raise(QWasmWindow *window)
@@ -181,16 +184,7 @@ void QWasmCompositor::setParent(QWasmWindow *window, QWasmWindow *parent)
{
m_compositedWindows[window].parentWindow = parent;
- requestRedraw();
-}
-
-void QWasmCompositor::flush(QWasmWindow *window, const QRegion &region)
-{
- QWasmCompositedWindow &compositedWindow = m_compositedWindows[window];
- compositedWindow.flushPending = true;
- compositedWindow.damage = region;
-
- requestRedraw();
+ requestUpdate();
}
int QWasmCompositor::windowCount() const
@@ -198,28 +192,6 @@ int QWasmCompositor::windowCount() const
return m_windowStack.count();
}
-
-void QWasmCompositor::redrawWindowContent()
-{
- // Redraw window content by sending expose events. This redraw
- // will cause a backing store flush, which will call requestRedraw()
- // to composit.
- for (QWasmWindow *platformWindow : m_windowStack) {
- QWindow *window = platformWindow->window();
- QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(
- window, QRect(QPoint(0, 0), window->geometry().size()));
- }
-}
-
-void QWasmCompositor::requestRedraw()
-{
- if (m_needComposit)
- return;
-
- m_needComposit = true;
- QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
-}
-
QWindow *QWasmCompositor::windowAt(QPoint globalPoint, int padding) const
{
int index = m_windowStack.count() - 1;
@@ -245,17 +217,6 @@ QWindow *QWasmCompositor::keyWindow() const
return m_windowStack.at(m_windowStack.count() - 1)->window();
}
-bool QWasmCompositor::event(QEvent *ev)
-{
- if (ev->type() == QEvent::UpdateRequest) {
- if (m_isEnabled)
- frame();
- return true;
- }
-
- return QObject::event(ev);
-}
-
void QWasmCompositor::blit(QOpenGLTextureBlitter *blitter, QWasmScreen *screen, const QOpenGLTexture *texture, QRect targetGeometry)
{
QMatrix4x4 m;
@@ -366,6 +327,97 @@ QRect QWasmCompositor::titlebarRect(QWasmTitleBarOptions tb, QWasmCompositor::Su
return rect;
}
+void QWasmCompositor::requestUpdateAllWindows()
+{
+ m_requestUpdateAllWindows = true;
+ requestUpdate();
+}
+
+void QWasmCompositor::requestUpdateWindow(QWasmWindow *window, UpdateRequestDeliveryType updateType)
+{
+ auto it = m_requestUpdateWindows.find(window);
+ if (it == m_requestUpdateWindows.end()) {
+ m_requestUpdateWindows.insert(window, updateType);
+ } else {
+ // Already registered, but upgrade ExposeEventDeliveryType to UpdateRequestDeliveryType.
+ // if needed, to make sure QWindow::updateRequest's are matched.
+ if (it.value() == ExposeEventDelivery && updateType == UpdateRequestDelivery)
+ it.value() = UpdateRequestDelivery;
+ }
+
+ requestUpdate();
+}
+
+// Requests an upate/new frame using RequestAnimationFrame
+void QWasmCompositor::requestUpdate()
+{
+ if (m_requestAnimationFrameId != -1)
+ return;
+
+ static auto frame = [](double frameTime, void *context) -> int {
+ Q_UNUSED(frameTime);
+ QWasmCompositor *compositor = reinterpret_cast<QWasmCompositor *>(context);
+ compositor->m_requestAnimationFrameId = -1;
+ compositor->deliverUpdateRequests();
+ return 0;
+ };
+ m_requestAnimationFrameId = emscripten_request_animation_frame(frame, this);
+}
+
+void QWasmCompositor::deliverUpdateRequests()
+{
+ // We may get new update requests during the window content update below:
+ // prepare for recording the new update set by setting aside the current
+ // update set.
+ auto requestUpdateWindows = m_requestUpdateWindows;
+ m_requestUpdateWindows.clear();
+ bool requestUpdateAllWindows = m_requestUpdateAllWindows;
+ m_requestUpdateAllWindows = false;
+
+ // Update window content, either all windows or a spesific set of windows. Use the correct update
+ // type: QWindow subclasses expect that requested and delivered updateRequests matches exactly.
+ m_inDeliverUpdateRequest = true;
+ if (requestUpdateAllWindows) {
+ for (QWasmWindow *window : m_windowStack) {
+ auto it = requestUpdateWindows.find(window);
+ UpdateRequestDeliveryType updateType =
+ (it == m_requestUpdateWindows.end() ? ExposeEventDelivery : it.value());
+ deliverUpdateRequest(window, updateType);
+ }
+ } else {
+ for (auto it = requestUpdateWindows.constBegin(); it != requestUpdateWindows.constEnd(); ++it) {
+ auto *window = it.key();
+ UpdateRequestDeliveryType updateType = it.value();
+ deliverUpdateRequest(window, updateType);
+ }
+ }
+ m_inDeliverUpdateRequest = false;
+
+ // Compose window content
+ frame();
+}
+
+void QWasmCompositor::deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType)
+{
+ // update by deliverUpdateRequest and expose event accordingly.
+ if (updateType == UpdateRequestDelivery) {
+ window->QPlatformWindow::deliverUpdateRequest();
+ } else {
+ QWindow *qwindow = window->window();
+ QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(
+ qwindow, QRect(QPoint(0, 0), qwindow->geometry().size()));
+ }
+}
+
+void QWasmCompositor::handleBackingStoreFlush()
+{
+ // Request update to flush the updated backing store content,
+ // unless we are currently processing an update, in which case
+ // the new content will flushed as a part of that update.
+ if (!m_inDeliverUpdateRequest)
+ requestUpdate();
+}
+
int dpiScaled(qreal value)
{
return value * (qreal(qt_defaultDpiX()) / 96.0);
@@ -678,11 +730,6 @@ void QWasmCompositor::drawWindow(QOpenGLTextureBlitter *blitter, QWasmScreen *sc
void QWasmCompositor::frame()
{
- if (!m_needComposit)
- return;
-
- m_needComposit = false;
-
if (!m_isEnabled || m_windowStack.empty() || !screen())
return;
@@ -748,8 +795,7 @@ void QWasmCompositor::notifyTopWindowChanged(QWasmWindow *window)
return;
}
- requestRedraw();
-
+ requestUpdate();
}
QWasmScreen *QWasmCompositor::screen()
diff --git a/src/plugins/platforms/wasm/qwasmcompositor.h b/src/plugins/platforms/wasm/qwasmcompositor.h
index b579059344..1a51016c03 100644
--- a/src/plugins/platforms/wasm/qwasmcompositor.h
+++ b/src/plugins/platforms/wasm/qwasmcompositor.h
@@ -110,25 +110,24 @@ public:
void lower(QWasmWindow *window);
void setParent(QWasmWindow *window, QWasmWindow *parent);
- void flush(QWasmWindow *surface, const QRegion &region);
-
int windowCount() const;
- void redrawWindowContent();
- void requestRedraw();
-
QWindow *windowAt(QPoint globalPoint, int padding = 0) const;
QWindow *keyWindow() const;
- bool event(QEvent *event);
-
static QWasmTitleBarOptions makeTitleBarOptions(const QWasmWindow *window);
static QRect titlebarRect(QWasmTitleBarOptions tb, QWasmCompositor::SubControls subcontrol);
QWasmScreen *screen();
QOpenGLContext *context();
-private slots:
+ enum UpdateRequestDeliveryType { ExposeEventDelivery, UpdateRequestDelivery };
+ void requestUpdateAllWindows();
+ void requestUpdateWindow(QWasmWindow *window, UpdateRequestDeliveryType updateType = ExposeEventDelivery);
+ void requestUpdate();
+ void deliverUpdateRequests();
+ void deliverUpdateRequest(QWasmWindow *window, UpdateRequestDeliveryType updateType);
+ void handleBackingStoreFlush();
void frame();
private:
@@ -152,6 +151,10 @@ private:
bool m_isEnabled;
QSize m_targetSize;
qreal m_targetDevicePixelRatio;
+ QMap<QWasmWindow *, UpdateRequestDeliveryType> m_requestUpdateWindows;
+ bool m_requestUpdateAllWindows = false;
+ int m_requestAnimationFrameId = -1;
+ bool m_inDeliverUpdateRequest = false;
static QPalette makeWindowPalette();
diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp
index 0b11415fd3..3cc067bbe7 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.cpp
+++ b/src/plugins/platforms/wasm/qwasmscreen.cpp
@@ -216,7 +216,7 @@ void QWasmScreen::updateQScreenAndCanvasRenderSize()
QPoint position(rect["left"].as<int>() - offset.x(), rect["top"].as<int>() - offset.y());
setGeometry(QRect(position, cssSize.toSize()));
- m_compositor->redrawWindowContent();
+ m_compositor->requestUpdateAllWindows();
}
void QWasmScreen::canvasResizeObserverCallback(emscripten::val entries, emscripten::val)
diff --git a/src/plugins/platforms/wasm/qwasmwindow.cpp b/src/plugins/platforms/wasm/qwasmwindow.cpp
index 9c5f1e4e74..3819e8daa6 100644
--- a/src/plugins/platforms/wasm/qwasmwindow.cpp
+++ b/src/plugins/platforms/wasm/qwasmwindow.cpp
@@ -111,8 +111,6 @@ void QWasmWindow::setGeometry(const QRect &rect)
}
QWindowSystemInterface::handleGeometryChange(window(), r);
QPlatformWindow::setGeometry(r);
-
- QWindowSystemInterface::flushWindowSystemEvents();
invalidate();
}
@@ -128,7 +126,6 @@ void QWasmWindow::setVisible(bool visible)
else if (m_windowState & Qt::WindowMaximized)
newGeom = platformScreen()->availableGeometry();
}
- QPlatformWindow::setVisible(visible);
m_compositor->setVisible(this, visible);
@@ -366,7 +363,7 @@ QRegion QWasmWindow::titleControlRegion() const
void QWasmWindow::invalidate()
{
- m_compositor->requestRedraw();
+ m_compositor->requestUpdateWindow(this);
}
QWasmCompositor::SubControls QWasmWindow::activeSubControl() const
@@ -397,6 +394,11 @@ qreal QWasmWindow::devicePixelRatio() const
void QWasmWindow::requestUpdate()
{
+ if (m_compositor) {
+ m_compositor->requestUpdateWindow(this, QWasmCompositor::UpdateRequestDelivery);
+ return;
+ }
+
static auto frame = [](double time, void *context) -> int {
Q_UNUSED(time);
QWasmWindow *window = static_cast<QWasmWindow *>(context);