wasm: fix onLoaded delay functionality

onLoaded and the initial expose/paint should be sequenced
such that onLoaded is fired first, followed by the expose.
This makes sure that we don't spend any time on painting
frames before Qt is completely initialized.

Add a "requestUpdateHold" mode to QWasmCompositor (initially
on) which disables requestUpdate calls, as well
as releaseRequestUpdateHold() which enables requestUpdate
calls again. This is a one-way transition; the mode
can't be enabled again.

This amends commit f2e22774 which implemented the concept
of startup tasks, where onLoaded can be delayed until
for instance font loading has been completed. After
this commit the expose event and initial commit will
be delayed as well.

Change-Id: Icc784306726174fbabe8785d54485860e968745a
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
Reviewed-by: Piotr Wierciński <piotr.wiercinski@qt.io>
This commit is contained in:
Morten Sørvig 2024-01-26 11:46:26 +01:00
parent 74dac559c0
commit 5e5e6240c2
8 changed files with 63 additions and 12 deletions

View File

@ -218,9 +218,12 @@ QEventDispatcherWasm::QEventDispatcherWasm()
#if QT_CONFIG(thread)
g_mainThread = pthread_self();
#endif
// Call the "onLoaded" JavaScript callback, unless startup tasks
// have been registered which should complete first.
checkCallQtLoaded();
// have been registered which should complete first. Run async
// to make sure event dispatcher construction (in particular any
// subclass construction) has completed first.
runAsync(callOnLoadedIfRequired);
} else {
#if QT_CONFIG(thread)
std::lock_guard<std::mutex> lock(g_staticDataMutex);
@ -916,10 +919,10 @@ void QEventDispatcherWasm::registerStartupTask()
void QEventDispatcherWasm::completeStarupTask()
{
--g_startupTasks;
checkCallQtLoaded();
callOnLoadedIfRequired();
}
void QEventDispatcherWasm::checkCallQtLoaded()
void QEventDispatcherWasm::callOnLoadedIfRequired()
{
if (g_startupTasks > 0)
return;
@ -929,12 +932,16 @@ void QEventDispatcherWasm::checkCallQtLoaded()
return;
qtLoadedCalled = true;
QTimer::singleShot(0, [](){
emscripten::val qt = emscripten::val::module_property("qt");
if (qt.isUndefined())
return;
qt.call<void>("onLoaded");
});
Q_ASSERT(g_mainThreadEventDispatcher);
g_mainThreadEventDispatcher->onLoaded();
}
void QEventDispatcherWasm::onLoaded()
{
emscripten::val qt = emscripten::val::module_property("qt");
if (qt.isUndefined())
return;
qt.call<void>("onLoaded");
}
namespace {

View File

@ -59,7 +59,8 @@ public:
static void registerStartupTask();
static void completeStarupTask();
static void checkCallQtLoaded();
static void callOnLoadedIfRequired();
virtual void onLoaded();
protected:
virtual bool processPostedEvents();

View File

@ -4,6 +4,8 @@
#include "qwasmcompositor.h"
#include "qwasmwindow.h"
#include <private/qeventdispatcher_wasm_p.h>
#include <qpa/qwindowsysteminterface.h>
#include <emscripten/html5.h>
@ -41,6 +43,17 @@ void QWasmCompositor::setEnabled(bool enabled)
m_isEnabled = enabled;
}
// requestUpdate delivery is initially disabled at startup, while Qt completes
// startup tasks such as font loading. This function enables requestUpdate delivery
// again.
void QWasmCompositor::releaseRequesetUpdateHold()
{
if (!m_requestUpdateHoldEnabled)
return;
m_requestUpdateHoldEnabled = false;
requestUpdate();
}
void QWasmCompositor::requestUpdateWindow(QWasmWindow *window, UpdateRequestDeliveryType updateType)
{
auto it = m_requestUpdateWindows.find(window);
@ -62,6 +75,9 @@ void QWasmCompositor::requestUpdate()
if (m_requestAnimationFrameId != -1)
return;
if (m_requestUpdateHoldEnabled)
return;
static auto frame = [](double frameTime, void *context) -> int {
Q_UNUSED(frameTime);

View File

@ -31,8 +31,8 @@ public:
QWasmScreen *screen();
void setEnabled(bool enabled);
void releaseRequesetUpdateHold();
enum UpdateRequestDeliveryType { ExposeEventDelivery, UpdateRequestDelivery };
void requestUpdateWindow(QWasmWindow *window, UpdateRequestDeliveryType updateType = ExposeEventDelivery);
void handleBackingStoreFlush(QWindow *window);
@ -50,6 +50,7 @@ private:
bool m_isEnabled = true;
QMap<QWasmWindow *, UpdateRequestDeliveryType> m_requestUpdateWindows;
int m_requestAnimationFrameId = -1;
bool m_requestUpdateHoldEnabled = true;
bool m_inDeliverUpdateRequest = false;
};

View File

@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "qwasmeventdispatcher.h"
#include "qwasmintegration.h"
#include <QtGui/qpa/qwindowsysteminterface.h>
@ -15,4 +16,20 @@ bool QWasmEventDispatcher::processPostedEvents()
return QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::AllEvents);
}
void QWasmEventDispatcher::onLoaded()
{
// This function is called when the application is ready to paint
// the first frame. Send the qtlaoder onLoaded event first (via
// the base class implementation), and then enable/call requestUpdate
// to deliver a frame.
QEventDispatcherWasm::onLoaded();
// Make sure all screens have a defined size; and pick
// up size changes due to onLoaded event handling.
QWasmIntegration *wasmIntegration = QWasmIntegration::get();
wasmIntegration->resizeAllScreens();
wasmIntegration->releaseRequesetUpdateHold();
}
QT_END_NAMESPACE

View File

@ -12,6 +12,7 @@ class QWasmEventDispatcher : public QEventDispatcherWasm
{
protected:
bool processPostedEvents() override;
void onLoaded() override;
};
QT_END_NAMESPACE

View File

@ -206,6 +206,13 @@ void QWasmIntegration::removeBackingStore(QWindow* window)
m_backingStores.remove(window);
}
void QWasmIntegration::releaseRequesetUpdateHold()
{
for (const auto &elementAndScreen : m_screens) {
elementAndScreen.wasmScreen->compositor()->releaseRequesetUpdateHold();
}
}
#ifndef QT_NO_OPENGL
QPlatformOpenGLContext *QWasmIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
{

View File

@ -78,6 +78,7 @@ public:
void resizeAllScreens();
void loadLocalFontFamilies(emscripten::val families);
void removeBackingStore(QWindow* window);
void releaseRequesetUpdateHold();
static quint64 getTimestamp();
int touchPoints;