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. Pick-to: 6.3 Change-Id: I747d6f7ace86ceddaa18ab86b6a0ee833f98991b Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
This commit is contained in:
parent
e3e2674100
commit
eb62b6ac02
@ -90,7 +90,7 @@ void QWasmBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoi
|
||||
Q_UNUSED(offset);
|
||||
|
||||
m_dirty |= region;
|
||||
m_compositor->requestRedraw();
|
||||
m_compositor->handleBackingStoreFlush();
|
||||
}
|
||||
|
||||
void QWasmBackingStore::updateTexture()
|
||||
|
@ -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 ®ion)
|
||||
{
|
||||
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()
|
||||
|
@ -110,25 +110,24 @@ public:
|
||||
void lower(QWasmWindow *window);
|
||||
void setParent(QWasmWindow *window, QWasmWindow *parent);
|
||||
|
||||
void flush(QWasmWindow *surface, const QRegion ®ion);
|
||||
|
||||
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();
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user