tag:blogger.com,1999:blog-29977399071092533542024-03-13T21:56:42.045+10:00Qt, linux and everythingthoughts from a developer.
Author of Hands-on Mobile and Embedded Development with Qt 5 http://bit.ly/HandsOnMobileEmbeddeddeviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.comBlogger47125tag:blogger.com,1999:blog-2997739907109253354.post-82433877272966308432023-07-10T12:41:00.000+10:002023-07-10T12:41:43.376+10:00Qt6 Webassembly: QtMultimedia or, How to play a video in a web browser using Qt<h2 style="text-align: left;"><span style="font-family: inherit;">QtMultimedia in WebAssembly</span></h2><p><span style="font-family: inherit;">Since Qt 6.5.0, <a href="https://doc.qt.io/qt-6/qtmultimedia-index.html" target="_blank">QtMultimedia</a> has support for playing video in a <a href="https://doc.qt.io/qt-6/qgraphicsvideoitem.html" target="_blank">QGraphicsVideoItem</a> and <a href="https://doc.qt.io/qt-6/qgraphicsscene.html" target="_blank">QGraphicsScene</a>, as well as recording from a camera. You can use Qt to play video, access the camera from a web browser, thus simplifying deployment.</span></p><p><span style="font-family: inherit;">With anything WebAssembly, there are a few things that work differently from the desktop applications. </span><span style="font-family: inherit;">The javascript function that is used</span><span style="font-family: inherit;"> to gather the list of devices, </span><span style="font-family: inherit;"> </span><span style="font-family: verdana;">getUserMedia</span><span style="font-family: inherit;"> is asyncronous, returning a javascript Promise, so the list is not readily available. This function can take some amount of time, depending on device and browser</span>. I have found that sometimes, it is inexplicitly long. </p><p><span style="font-family: inherit;">You can connect to the </span><b style="font-family: inherit;"><a href="https://doc.qt.io/qt-6/qmediadevices.html#audioInputsChanged" target="_blank">QMediaDevices::audioInputsChanged</a></b><span style="font-family: inherit;">, </span><b style="font-family: inherit;"><a href="https://doc.qt.io/qt-6/qmediadevices.html#videoInputsChanged" target="_blank">QMediaDevices::videoInputsChanged</a></b><span style="font-family: inherit;"> and </span><b style="font-family: inherit;"><a href="https://doc.qt.io/qt-6/qmediadevices.html#audioOutputsChanged" target="_blank">QMediaDevices::audioOutputsChanged</a></b><span style="font-family: inherit;"> signals to discover when they are available.</span></p><h2 style="text-align: left;"><span style="font-family: inherit;">QPermission</span></h2><p><span style="font-family: inherit;">Browser permissions are required for accessing the microphone and camera, and a secure https context is a prerequisite for granting them. If this is not run or permission is not granted by the user, the list of available devices will be missing the label, or name, of any device not given permission and it will be unavailable for use.</span></p><p><span style="font-family: inherit;">Using the new <a href="https://doc.qt.io/qt-6/qpermission.html" target="_blank">QPermission</a> API which will be introduced in Qt 6.5 will make sure your app is permissions ready. The example QtMultimedia apps have been changed in Qt 6.6 to use this API.</span></p><p><span style="font-family: inherit;">You do not need to use QPermissions in wasm, because it uses the same function - getUserMedia which causes the browser to ask for permissions.</span></p><p><span style="font-family: inherit;">See </span><a href="https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API" style="font-family: inherit;" target="_blank">https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API</a></p><h2 style="text-align: left;"><span style="font-family: inherit;">Camera</span></h2><p style="text-align: left;"><span style="font-family: inherit;">The camera (and microphone) need user permissions. Something like this will work for granting permissions to use the camera:</span></p><div><span style="font-family: inherit;"><br /></span></div><div><div><span style="font-family: verdana;"><span style="font-size: x-small;"> </span><span style="font-size: x-small;"> QCameraPermission cameraPermission;</span></span></div><div><span style="font-family: verdana; font-size: x-small;"> switch (qApp->checkPermission(cameraPermission)) {</span></div><div><span style="font-family: verdana; font-size: x-small;"> case Qt::PermissionStatus::Undetermined:</span></div><div><span style="font-family: verdana; font-size: x-small;"> qApp->requestPermission(cameraPermission, this, &MainWindow::init);</span></div><div><span style="font-family: verdana; font-size: x-small;"> return;</span></div><div><span style="font-family: verdana; font-size: x-small;"> case Qt::PermissionStatus::Denied:</span></div><div><span style="font-family: verdana; font-size: x-small;"> qWarning("Camera permission is not granted!");</span></div><div><span style="font-family: verdana; font-size: x-small;"> return;</span></div><div><span style="font-family: verdana; font-size: x-small;"> case Qt::PermissionStatus::Granted:</span></div><div><span style="font-family: verdana; font-size: x-small;"> break;</span></div><div><span style="font-family: verdana; font-size: x-small;"> }</span></div></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: inherit;">What this can look like in the browser:</span></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAU3lM8mmefrBdV0TmbiyQXT6Ipw5blsALbc54I27QM4iQy01355_JAyi1ioxyfv0Rd_S_EF5DP1Zd15CY_TXILGPd_UWsSs3G6nnAJGSpofBdXRMyLbmEmOlwL5rzuPH997T0QtV2mKHNzo88BHNGx1USzdY5zIzeLcYCXYn5LX_dYIGGumuhXfiRlKs/s438/Screenshot%202023-06-19%20at%204.52.01%20pm.png" style="margin-left: 1em; margin-right: 1em;"><span style="font-family: inherit;"><img border="0" data-original-height="260" data-original-width="438" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAU3lM8mmefrBdV0TmbiyQXT6Ipw5blsALbc54I27QM4iQy01355_JAyi1ioxyfv0Rd_S_EF5DP1Zd15CY_TXILGPd_UWsSs3G6nnAJGSpofBdXRMyLbmEmOlwL5rzuPH997T0QtV2mKHNzo88BHNGx1USzdY5zIzeLcYCXYn5LX_dYIGGumuhXfiRlKs/s320/Screenshot%202023-06-19%20at%204.52.01%20pm.png" width="320" /></span></a></div><p style="text-align: left;"><br /></p><p style="text-align: left;"><span style="font-family: inherit;">When you connect to the signal QMediaDevices::videoInputChanged, it will be emitted when the list of camera devices are available. </span></p><div style="text-align: left;"><span style="font-size: x-small;"><span style="font-family: inherit;"> </span><span> <span style="font-family: verdana;"> </span></span></span><span><span style="font-family: verdana; font-size: x-small;">connect(m_mediaDevices, &QMediaDevices::videoInputsChanged, [this]() { doCamera();});</span></span></div><div style="text-align: left;"><span><span style="font-family: inherit; font-size: x-small;"><br /></span></span></div><div style="text-align: left;"><span style="font-family: inherit;">Here is a screenshot of the webcam showing in a test app I used for developing this feature. The sources for this are in </span><span style="font-family: courier;">qtmultimedia/tests/manual/wasm/camera</span></div><div style="text-align: left;"><span style="font-family: inherit;"><br /></span></div><div style="text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvWy7wHFLmXKdfb_mrhIdR4NvmV1vQNUkfQzIExGc7Hm0SO3emGr4hfmpO-HKPvn5RWofT_oP4lbf74euwCJnD3YApp-LPh6ZihkNaeJZ96H0mQS3eVtpih9IuIXhFnNftK2CDddKCvd_PKdEQv1rGh4X6x74nSzEX8pn9v91K56h2mgmZZVFPNRCzXB8/s1122/Screenshot%202023-06-19%20at%204.53.08%20pm.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><span style="font-family: inherit;"><img border="0" data-original-height="824" data-original-width="1122" height="235" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvWy7wHFLmXKdfb_mrhIdR4NvmV1vQNUkfQzIExGc7Hm0SO3emGr4hfmpO-HKPvn5RWofT_oP4lbf74euwCJnD3YApp-LPh6ZihkNaeJZ96H0mQS3eVtpih9IuIXhFnNftK2CDddKCvd_PKdEQv1rGh4X6x74nSzEX8pn9v91K56h2mgmZZVFPNRCzXB8/s320/Screenshot%202023-06-19%20at%204.53.08%20pm.png" width="320" /></span></a></div><div style="text-align: left;"><span style="font-family: inherit;"><br /></span></div><p style="text-align: left;"><span style="font-family: inherit;">You can try it out for yourself! <a href="https://lpotter.github.io/camera/camera-test.html" target="_blank">camera-test</a></span></p><p style="text-align: left;">(Chrome 114 seems to have issue with VideoFrame, working with Chrome 117) </p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: inherit;"><br /></span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: inherit;"><h2 style="text-align: left;">Video</h2><p style="text-align: left;">Using Javascript's <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API" target="_blank">WebCodecs API</a>, we are be able to leverage Javascript's <a href="https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame" target="_blank">VideoFrame API</a>, which is currently available on Chrome and Safari based browsers.</p><p style="text-align: left;">Permissions are not needed to play video.</p><p style="text-align: left;">Video currently only works with QWidget based apps. Playing video in quick apps will be fixed in an upcoming version of Qt. Here's a screenshot of the test app I used to develop it:</p><p></p><div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-O44dv5KAb_Kz9Og1Goy7zmchVNcG7U6omO6haRD6_X14WfkqJK28Fk-qfvXjRcJXgcjI8j-a8qU7n3VNhQz2zdVLdJWD21PK5fJaZnXCHywndnMb8So7iNi0ISpBQdRO10vhnmi0Pn4ZsEM8HsmAmKJvOfhLV0C3r5_o8zpAnoEbw5dXRPfhSsi5A5I/s1259/Screenshot%202023-06-19%20at%204.28.42%20pm.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="900" data-original-width="1259" height="229" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-O44dv5KAb_Kz9Og1Goy7zmchVNcG7U6omO6haRD6_X14WfkqJK28Fk-qfvXjRcJXgcjI8j-a8qU7n3VNhQz2zdVLdJWD21PK5fJaZnXCHywndnMb8So7iNi0ISpBQdRO10vhnmi0Pn4ZsEM8HsmAmKJvOfhLV0C3r5_o8zpAnoEbw5dXRPfhSsi5A5I/s320/Screenshot%202023-06-19%20at%204.28.42%20pm.png" width="320" /></a></div><div class="separator" style="clear: both;"><br /></div></span></div><p style="clear: both; text-align: center;"><span style="font-family: inherit;"></span></p><div style="text-align: left;"><span style="font-family: inherit;"><span style="font-family: inherit;">Video recording also works and will save the file to the browsers local filesystem, which has a limited capacity. It can then be played as normal or downloaded to the system filesystem via </span><a href="https://doc.qt.io/qt-6/qfiledialog.html#saveFileContent" style="font-family: inherit;" target="_blank">QFileDialog:SaveFileContent</a></span></div><p></p><p></p><p><span style="font-family: inherit;"><br /></span></p><div class="separator" style="clear: both; text-align: center;"><span style="font-family: inherit;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dxGuQMzASBCZ8X4-VdrOwWx3QidWBtvxZuytMHBSM5ChwiJkOtA8CNqoptXPljiT_ku1F-tyoFg-8VrrKU1Kg' class='b-hbp-video b-uploaded' frameborder='0'></iframe></span></div><div class="separator" style="clear: both; text-align: center;"><span style="font-family: inherit;"><br /></span></div><div class="separator" style="clear: both; text-align: center;"><br /></div><h2 style="text-align: left;"><span><span style="font-family: inherit; font-size: small;">Known Issues</span></span></h2><div><ul style="text-align: left;"><li><span style="font-family: inherit;">List of media devices is not readily available for application.</span></li><li><span style="font-family: inherit;">Video does not show in QtQuick declarative apps</span></li></ul><p></p><p><span style="font-family: inherit;"><br /></span></p><p><span style="font-family: inherit;"><br /></span></p><p><br /></p></div>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-30417649428148178152022-03-09T08:42:00.000+10:002022-03-09T08:42:17.279+10:00Qt for WebAssembly on mobile devices<p>Qt for WebAssembly on mobile devices, specifically phones, has lacked an essential feature - support for the native keyboard. It may or may not have worked. If it worked, it did not work very well. The tricky issue is opening the keyboard when needed and closing when it wasn't. There is no simple API for doing that on any platform we target - iOS, Android and Windows.</p><p>Although it feels a bit hacky to me to open the keyboard using javascript , we use Emscripten's C++ interface to create a hidden javascript text input element and set focus to that, which opens the platform keyboard.</p><p>Emscripten on Android had one other issue - the usual Emscripten input event API was not working. Nothing being typed on the native virtual keyboard was being handled like on other platforms. I found I could utilize the hidden input element that is used to pop up the keyboard, to listen for input characters and then send them to Qt. </p><p>As it is, <a href="https://codereview.qt-project.org/c/qt/qtbase/+/384722" target="_blank">this patch</a> adds support for native mobile keyboard for iOS, Android and Windows on Qt for WebAssembly. (now merged into dev as 66a76a5def46d0e4a330f7130ad440c639b87cf7), too late to make it into 6.3.</p><p>Other issues on mobile are memory (as usual). Up until recently, Safari limits browser memory to much less than the other browsers. </p><p>Other areas that Qt covers for mobile devices that do not work on Qt for WebAssembly yet are sensors and bluetooth connectivity. Although there is a patch for some sensors support, it has not been merged and probably needs updating. It may not work at all, either.</p><p>Bluetooth connectivity for javascript is currently only supported on the Chrome browser and is currently experimental. While I could probably add this support to Qt WebAssembly, it would not get merged and the API might be too changey.</p><p>Any areas you find Qt WebAssembly is lacking on mobile, please report to <a href="https://bugreports.qt.io/">https://bugreports.qt.io/</a></p><p><br /></p>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com1tag:blogger.com,1999:blog-2997739907109253354.post-85158804210288765022022-01-07T16:42:00.000+10:002022-01-07T16:42:40.374+10:00Qt WebAssembly clipboard<script type="text/javascript">
var spector;
var captureOnLoad = false;
var captureOffScreen = false;
window.__SPECTOR_Canvases = [];
(function() {
var __SPECTOR_Origin_EXTENSION_GetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.__SPECTOR_Origin_EXTENSION_GetContext = __SPECTOR_Origin_EXTENSION_GetContext;
if (typeof OffscreenCanvas !== 'undefined') {
var __SPECTOR_Origin_EXTENSION_OffscreenGetContext = OffscreenCanvas.prototype.getContext;
OffscreenCanvas.prototype.__SPECTOR_Origin_EXTENSION_OffscreenGetContext = __SPECTOR_Origin_EXTENSION_OffscreenGetContext;
OffscreenCanvas.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
// context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
this.id = "Offscreen";
window.__SPECTOR_Canvases.push(this);
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
}
HTMLCanvasElement.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
if (captureOffScreen) {
var found = false;
for (var i = 0; i < window.__SPECTOR_Canvases.length; i++) {
if (window.__SPECTOR_Canvases[i] === this) {
found = true;
break;
}
}
if (!found) {
window.__SPECTOR_Canvases.push(this);
}
}
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (this.parentElement || false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
})()</script><p>Clipboard use on the desktop platforms is ubiquitous. Most people use it without thinking. Copy, Paste and Cut keyboard strokes are in-grained into muscle memory. </p><p>On the web it can present security issues as someone could read or write to your clipboard without you knowing.</p><p>Up until now, Qt for WebAssembly's clipboard was text only and only within the app itself. Qt 6.3 will have better clipboard support between host and app but also adds copy/pasting of images.</p><p>WebAssembly is a sandboxed platform like javascript. There are some extra security hurdles in doing some common things such as copy and paste of binary data such as images. One issue is clipboard use between the host platform and the browser sandbox. Allowing the web app to have access to the clipboard in which it could send arbitrary data without the user knowing could be dangerous for the user.</p><p>Browsers generally allow clipboard during user generated events such as when a user makes common key sequences such as [ctrl | command] c - the copy keys.</p><p>Qt itself has support for programmatically copying text and binary data such as images and works great on the desktop, but which presents issues for web browsers. There are work-arounds, like using a hidden javascript element and the javascript function execCommand to "copy". However, this function has been depreciated. </p><p>By using the asynchronous<a href="https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API" target="_blank"> Clipboard API </a>and making use of javascript clipboard events where possible, we can bring image clipboard support to Qt WebAssembly. The Clipboard API requires a secure context (https) for full feature use. Among other things, the Clipboard API allows image and arbitrary data to be copied and pasted. Whereas before, only text mime types were supported. </p><p>This API is of course implemented in the different browsers in different ways. On Firefox, read() and write() are only partially implemented and is hidden behind about:config settings. As well, copy/paste of arbitrary binary data does not seem to be supported and mostly silently fails.<br /></p><p>Here are the ways browsers support Clipboard API:</p><p><span face="arial, x-locale-body, sans-serif" style="background-color: #f4f4f4; color: #1b1b1b; font-size: 20px; letter-spacing: -0.0555556px;"><b>Firefox</b> </span></p><ul style="text-align: left;"><li>write() is available without permission in secure contexts and browser extensions, but only from user-initiated event callbacks.</li><li>Clipboard API</li><ul><li>secure context (https or localhost)</li><li>dom.events.asyncClipboard</li><li>dom.events.asyncClipboard.clipboardItem</li><li>dom.events.asyncClipboard.read</li><li>dom.events.testing.asyncClipboard</li></ul></ul><b>Safari</b><p></p><div><ul style="text-align: left;"><li><span>Clipboard API </span></li><ul><li><span>secure context </span></li></ul></ul></div><div><b>Chrome</b></div><div><ul style="text-align: left;"><li>Clipboard API </li><ul><li>secure context</li><li>user permissions</li></ul><li>read() only supports</li><ul><li>text/html</li><li>text/png</li></ul></ul><div><br /></div><div>Also included in the now merged commit f0be152896471aa392bb1b2b649b66feb31480cc is a clipboard manual test app that can be used on both desktop and webassembly to test clipbpoard use.</div></div><div><br /></div><div>You can use the clipboard without a secure https context, but you won't get interaction between host and web app.</div><div><br /></div>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-57821236821829563342021-08-18T10:38:00.000+10:002021-08-18T10:38:43.088+10:00Qt Multimedia has a new friend...<script type="text/javascript">
var spector;
var captureOnLoad = false;
var captureOffScreen = false;
window.__SPECTOR_Canvases = [];
(function() {
var __SPECTOR_Origin_EXTENSION_GetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.__SPECTOR_Origin_EXTENSION_GetContext = __SPECTOR_Origin_EXTENSION_GetContext;
if (typeof OffscreenCanvas !== 'undefined') {
var __SPECTOR_Origin_EXTENSION_OffscreenGetContext = OffscreenCanvas.prototype.getContext;
OffscreenCanvas.prototype.__SPECTOR_Origin_EXTENSION_OffscreenGetContext = __SPECTOR_Origin_EXTENSION_OffscreenGetContext;
OffscreenCanvas.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
// context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
this.id = "Offscreen";
window.__SPECTOR_Canvases.push(this);
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
}
HTMLCanvasElement.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
if (captureOffScreen) {
var found = false;
for (var i = 0; i < window.__SPECTOR_Canvases.length; i++) {
if (window.__SPECTOR_Canvases[i] === this) {
found = true;
break;
}
}
if (!found) {
window.__SPECTOR_Canvases.push(this);
}
}
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (this.parentElement || false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
})()</script><p> QtMultimedia just got a new platform to run on - Qt for WebAssembly!</p><p><br /></p><p>I am happy to announce a new platform contribution for Qt Multimedia.<br /></p><p>As of change <a href="https://codereview.qt-project.org/c/qt/qtmultimedia/+/348150">https://codereview.qt-project.org/c/qt/qtmultimedia/+/348150</a> and very much thanks to Qt contributor, <span><span class="text "><span class="name">Raffaele Pertile, we now have audio play and record on the WebAssembly platform for Qt 6.2.</span></span></span></p><p><span><span class="text "><span class="name">This is possible as Emscripten has built-in support for </span></span></span><span><span class="text "><span class="name"><span id="output">OpenAL, which this patch uses to push audio data around. So now, all your Qt webAssembly apps can have sound effects! \0/</span></span></span></span></p><p><span><span class="text "><span class="name"><span id="output"> </span></span></span></span></p><p><span><span class="text "><span class="name"><span id="output"> </span> </span></span></span></p><p><span><span class="text "><span class="name"> </span></span></span> </p>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-84265444465695914352021-07-12T09:00:00.006+10:002021-07-12T09:31:24.596+10:00Qt WebAssembly: prompt on exit<script type="text/javascript">
var spector;
var captureOnLoad = false;
var captureOffScreen = false;
window.__SPECTOR_Canvases = [];
(function() {
var __SPECTOR_Origin_EXTENSION_GetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.__SPECTOR_Origin_EXTENSION_GetContext = __SPECTOR_Origin_EXTENSION_GetContext;
if (typeof OffscreenCanvas !== 'undefined') {
var __SPECTOR_Origin_EXTENSION_OffscreenGetContext = OffscreenCanvas.prototype.getContext;
OffscreenCanvas.prototype.__SPECTOR_Origin_EXTENSION_OffscreenGetContext = __SPECTOR_Origin_EXTENSION_OffscreenGetContext;
OffscreenCanvas.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
// context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
this.id = "Offscreen";
window.__SPECTOR_Canvases.push(this);
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
}
HTMLCanvasElement.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
if (captureOffScreen) {
var found = false;
for (var i = 0; i < window.__SPECTOR_Canvases.length; i++) {
if (window.__SPECTOR_Canvases[i] === this) {
found = true;
break;
}
}
if (!found) {
window.__SPECTOR_Canvases.push(this);
}
}
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (this.parentElement || false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
})()</script><p> I sometimes get asked if there is a way to let a Qt app ask the user if they really want to close the app when the browser tab or window gets closed by the user.</p><p><br /></p><p>The answer is yes and no. Yes, in that there is, and no in that it won't be your own Qt dialog/prompt.</p><p><br /></p><p>We can use javascript in Qt webassembly apps, so the prompt will be opened by the browsers javascript. </p><p><br /></p><p>How?</p><p>There are two ways to run javascript. By using emscripten macro EM_ASM, or including emscripten/val.h and using straight c++.</p><p><br /></p><p><b>EM_ASM:</b></p><p>The is the easiest way but uses a macro.</p><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre><pre style="margin-bottom: 0px; margin-top: 0px;">#<span style="color: navy;">include</span><span style="color: silver;"> </span><<span style="color: green;">emscripten</span>.<span style="color: green;">h</span>></pre><p>Just add something like this, in your app constructor function:</p><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: silver;"> </span>EM_ASM(</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: silver;"> </span>window.onbeforeunload<span style="color: silver;"> </span>=<span style="color: silver;"> </span>function<span style="color: silver;"> </span>(e)<span style="color: silver;"> </span>{</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: silver;"> </span>e<span style="color: silver;"> </span>=<span style="color: silver;"> </span>e<span style="color: silver;"> </span>||<span style="color: silver;"> </span>window.event;</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: silver;"> </span><span style="color: olive;">return</span><span style="color: silver;"> </span><span style="color: green;">'Sure'</span>;</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: silver;"> </span>};</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: silver;"> </span>);</pre><h3 style="text-align: left;"><b><br /></b></h3><h3 style="text-align: left;"><b>C++:</b></h3><p>This is a bit more complicated, and involves a global static function.</p><pre style="margin-bottom: 0px; margin-top: 0px;">#<span style="color: navy;">include</span><span style="color: silver;"> </span><<span style="color: green;">emscripten</span>/<span style="color: green;">bind</span>.<span style="color: green;">h</span>></pre><pre style="margin-bottom: 0px; margin-top: 0px;">#<span style="color: navy;">include</span><span style="color: silver;"> </span><<span style="color: green;">emscripten</span>/<span style="color: green;">val</span>.<span style="color: green;">h</span>></pre><pre style="margin-bottom: 0px; margin-top: 0px;">#<span style="color: navy;">include</span><span style="color: silver;"> </span><<span style="color: green;">emscripten</span>.<span style="color: green;">h</span>></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: olive;">static</span><span style="color: silver;"> </span>emscripten::val<span style="color: silver;"> </span>mybrowserBeforeUnload(emscripten::val<span style="color: silver;"> </span>e)</pre><pre style="margin-bottom: 0px; margin-top: 0px;">{</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: silver;"> </span><span style="color: olive;">return</span><span style="color: silver;"> </span>emscripten::val(<span style="color: green;">"Sure"</span>);</pre><pre style="margin-bottom: 0px; margin-top: 0px;">}</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre><pre style="margin-bottom: 0px; margin-top: 0px;">EMSCRIPTEN_BINDINGS(app) // must be unique name!</pre><pre style="margin-bottom: 0px; margin-top: 0px;">{</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: silver;"> </span>function(<span style="color: green;">"myBrowserBeforeUnload"</span>,<span style="color: silver;"> </span>&mybrowserBeforeUnload);</pre><pre style="margin-bottom: 0px; margin-top: 0px;">}</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre><p style="margin-bottom: 0px; margin-top: 0px; text-align: left;">and in the c'tor:</p><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><span style="color: silver;"> </span>emscripten::val::global(<span style="color: green;">"window"</span>).set(<span style="color: green;">"onbeforeunload"</span>,<span style="color: silver;"> </span>emscripten::val::module_property(<span style="color: green;">"myBrowserBeforeUnload"</span>));</pre><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre><p style="margin-bottom: 0px; margin-top: 0px; text-align: left;">The key is to return a string. Any string will do.</p><p style="margin-bottom: 0px; margin-top: 0px; text-align: left;">and it looks something like this:</p><p style="margin-bottom: 0px; margin-top: 0px; text-align: left;"><br /></p><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-L1xNOTa4QkY/YOt36j_eK8I/AAAAAAAAn-E/Xgl8z9l_rnAGYMtSSepNW4aDcOAOn8dLACLcBGAsYHQ/s452/exit_prompt.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="138" data-original-width="452" src="https://1.bp.blogspot.com/-L1xNOTa4QkY/YOt36j_eK8I/AAAAAAAAn-E/Xgl8z9l_rnAGYMtSSepNW4aDcOAOn8dLACLcBGAsYHQ/s320/exit_prompt.jpg" width="320" /></a></div><br /><p style="margin-bottom: 0px; margin-top: 0px; text-align: left;"><br /></p><pre style="margin-bottom: 0px; margin-top: 0px;"><br /></pre></pre>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com1tag:blogger.com,1999:blog-2997739907109253354.post-6532145408770544462021-06-13T09:51:00.000+10:002021-06-13T09:51:08.204+10:00(redux) Qt WebAssembly performance enhancement <p> In my last post <a href="https://qtandeverything.blogspot.com/2021/06/qt-webassembly-performance-enhancement.html" target="_blank">Qt WebAssembly performance enhancement</a></p><p>there were some impressive performance stat speedups. Unfortunately, as my collegue Morten pointed out, both builds were in <b>debug</b> mode. *sigh*</p><p>So I rebuilt them in release mode, and added a few selected benchmarks from the Qt tests/benchmark source directory:</p><p></p><ul style="text-align: left;"><li>tst_affectors</li><li>tst_emission</li><li>tst_QGraphicsScene</li><li>tst_QGraphicsView</li><li>tst_QGraphicsWidget</li><li>tst_qanimation</li><li>tst_QMatrix4x4</li><li>BlendBench</li><li>tst_QImageConversion</li><li>tst_DrawTexture</li><li>tst_QPainter</li></ul><p></p><p>Although not as impressive overall, there is still quite a speed up in the image conversions and QPainter areas, for example:</p><p><b>non-simd:</b></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(BGR30 on RGB32, (1000x1000), circle)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"BGR30 on RGB32, (1000x1000), circle":</span></p><p><span style="font-size: xx-small;"> 2.3 msecs per iteration (total: 76, iterations: 32)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(BGR30 on RGB32, (1000x1000), line)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"BGR30 on RGB32, (1000x1000), line":</span></p><p><span style="font-size: xx-small;"> 2.4 msecs per iteration (total: 77, iterations: 32)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(BGR30 on RGB32, (1000x1000), solidrect)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"BGR30 on RGB32, (1000x1000), solidrect":</span></p><p><span style="font-size: xx-small;"> 2.4 msecs per iteration (total: 78, iterations: 32)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(BGR30 on RGB32, (1000x1000), alpharect)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"BGR30 on RGB32, (1000x1000), alpharect":</span></p><p><span style="font-size: xx-small;"> 2.4 msecs per iteration (total: 78, iterations: 32)</span></p><p><br /></p><p><b>simd:</b></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"BGR30 on RGB32, (1000x1000), circle":</span></p><p><span style="font-size: xx-small;"> 0.95 msecs per iteration (total: 61, iterations: 64)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(BGR30 on RGB32, (1000x1000), line)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"BGR30 on RGB32, (1000x1000), line":</span></p><p><span style="font-size: xx-small;"> 0.95 msecs per iteration (total: 61, iterations: 64)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(BGR30 on RGB32, (1000x1000), solidrect)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"BGR30 on RGB32, (1000x1000), solidrect":</span></p><p><span style="font-size: xx-small;"> 0.92 msecs per iteration (total: 59, iterations: 64)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(BGR30 on RGB32, (1000x1000), alpharect)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"BGR30 on RGB32, (1000x1000), alpharect":</span></p><p><span style="font-size: xx-small;"> 0.95 msecs per iteration (total: 61, iterations: 64</span></p><p><br /></p><p><b>non-simd:</b></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(ARGB32_pm on RGB32, (1000x1000), circle)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"ARGB32_pm on RGB32, (1000x1000), circle":</span></p><p><span style="font-size: xx-small;"> 1.7 msecs per iteration (total: 56, iterations: 32)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(ARGB32_pm on RGB32, (1000x1000), line)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"ARGB32_pm on RGB32, (1000x1000), line":</span></p><p><span style="font-size: xx-small;"> 1.7 msecs per iteration (total: 55, iterations: 32)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(ARGB32_pm on RGB32, (1000x1000), solidrect)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"ARGB32_pm on RGB32, (1000x1000), solidrect":</span></p><p><span style="font-size: xx-small;"> 1.7 msecs per iteration (total: 55, iterations: 32)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(ARGB32_pm on RGB32, (1000x1000), alpharect)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"ARGB32_pm on RGB32, (1000x1000), alpharect":</span></p><p><span style="font-size: xx-small;"> 3.6 msecs per iteration (total: 58, iterations: 16)</span></p><p><br /></p><p><b>simd:</b></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(ARGB32_pm on RGB32, (1000x1000), circle)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"ARGB32_pm on RGB32, (1000x1000), circle":</span></p><p><span style="font-size: xx-small;"> 2.6 msecs per iteration (total: 85, iterations: 32)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(ARGB32_pm on RGB32, (1000x1000), line)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"ARGB32_pm on RGB32, (1000x1000), line":</span></p><p><span style="font-size: xx-small;"> 4.0 msecs per iteration (total: 64, iterations: 16)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(ARGB32_pm on RGB32, (1000x1000), solidrect)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"ARGB32_pm on RGB32, (1000x1000), solidrect":</span></p><p><span style="font-size: xx-small;"> 2.2 msecs per iteration (total: 71, iterations: 32)</span></p><p><span style="font-size: xx-small;">PASS : tst_QPainter::drawPixmap(ARGB32_pm on RGB32, (1000x1000), alpharect)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QPainter::drawPixmap():"ARGB32_pm on RGB32, (1000x1000), alpharect":</span></p><p><span style="font-size: xx-small;"> 4.5 msecs per iteration (total: 73, iterations: 16)</span></p><p><br /></p><p>and image conversions:</p><p><b>non-simd:</b></p><p><span style="font-size: xx-small;">PASS : tst_QImageConversion::convertGenericInplace(argb32 -> argb32pm -> argb32)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QImageConversion::convertGenericInplace():"argb32 -> argb32pm -> argb32":</span></p><p><span style="font-size: xx-small;"> 6.1 msecs per iteration (total: 98, iterations: 16)</span></p><p><span style="font-size: xx-small;">PASS : tst_QImageConversion::convertGenericInplace(argb32 -> rgb32 -> argb32)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QImageConversion::convertGenericInplace():"argb32 -> rgb32 -> argb32":</span></p><p><span style="font-size: xx-small;"> 2.9 msecs per iteration (total: 94, iterations: 32)</span></p><p><span style="font-size: xx-small;">PASS : tst_QImageConversion::convertGenericInplace(argb32 -> rgba8888 -> argb32)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QImageConversion::convertGenericInplace():"argb32 -> rgba8888 -> argb32":</span></p><p><span style="font-size: xx-small;"> 4.6 msecs per iteration (total: 75, iterations: 16)</span></p><p><b>simd:</b></p><p><span style="font-size: xx-small;">PASS : tst_QImageConversion::convertGenericInplace(argb32 -> argb32pm -> argb32)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QImageConversion::convertGenericInplace():"argb32 -> argb32pm -> argb32":</span></p><p><span style="font-size: xx-small;"> 4.2 msecs per iteration (total: 68, iterations: 16)</span></p><p><span style="font-size: xx-small;">PASS : tst_QImageConversion::convertGenericInplace(argb32 -> rgb32 -> argb32)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QImageConversion::convertGenericInplace():"argb32 -> rgb32 -> argb32":</span></p><p><span style="font-size: xx-small;"> 0.49 msecs per iteration (total: 63, iterations: 128)</span></p><p><span style="font-size: xx-small;">PASS : tst_QImageConversion::convertGenericInplace(argb32 -> rgba8888 -> argb32)</span></p><p><span style="font-size: xx-small;">RESULT : tst_QImageConversion::convertGenericInplace():"argb32 -> rgba8888 -> argb32":</span></p><p><span style="font-size: xx-small;"> 0.90 msecs per iteration (total: 58, iterations: 64)</span></p><p><br /></p><p><br /></p><p>But others were slower for the simd build. Probably due to emscripten not fully supporting simd instructions and emulating those where it doesn't support.</p><p><br /></p><p>For full benchmark results get the <a href="https://drive.google.com/file/d/1DRrO5_IRsM34kzuQo-w5xdryEQ1gQmUi/view?usp=sharing">zip file</a></p><p><br /></p><p><br /></p><p><br /></p>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-22964205374535786252021-06-10T19:34:00.002+10:002021-06-10T19:34:21.624+10:00Qt WebAssembly performance enhancement<script type="text/javascript">
var spector;
var captureOnLoad = false;
var captureOffScreen = false;
window.__SPECTOR_Canvases = [];
(function() {
var __SPECTOR_Origin_EXTENSION_GetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.__SPECTOR_Origin_EXTENSION_GetContext = __SPECTOR_Origin_EXTENSION_GetContext;
if (typeof OffscreenCanvas !== 'undefined') {
var __SPECTOR_Origin_EXTENSION_OffscreenGetContext = OffscreenCanvas.prototype.getContext;
OffscreenCanvas.prototype.__SPECTOR_Origin_EXTENSION_OffscreenGetContext = __SPECTOR_Origin_EXTENSION_OffscreenGetContext;
OffscreenCanvas.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
// context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
this.id = "Offscreen";
window.__SPECTOR_Canvases.push(this);
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
}
HTMLCanvasElement.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
if (captureOffScreen) {
var found = false;
for (var i = 0; i < window.__SPECTOR_Canvases.length; i++) {
if (window.__SPECTOR_Canvases[i] === this) {
found = true;
break;
}
}
if (!found) {
window.__SPECTOR_Canvases.push(this);
}
}
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (this.parentElement || false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
})()</script><script type="text/javascript">
var spector;
var captureOnLoad = false;
var captureOffScreen = false;
window.__SPECTOR_Canvases = [];
(function() {
var __SPECTOR_Origin_EXTENSION_GetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.__SPECTOR_Origin_EXTENSION_GetContext = __SPECTOR_Origin_EXTENSION_GetContext;
if (typeof OffscreenCanvas !== 'undefined') {
var __SPECTOR_Origin_EXTENSION_OffscreenGetContext = OffscreenCanvas.prototype.getContext;
OffscreenCanvas.prototype.__SPECTOR_Origin_EXTENSION_OffscreenGetContext = __SPECTOR_Origin_EXTENSION_OffscreenGetContext;
OffscreenCanvas.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
// context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
this.id = "Offscreen";
window.__SPECTOR_Canvases.push(this);
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
}
HTMLCanvasElement.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
if (captureOffScreen) {
var found = false;
for (var i = 0; i < window.__SPECTOR_Canvases.length; i++) {
if (window.__SPECTOR_Canvases[i] === this) {
found = true;
break;
}
}
if (!found) {
window.__SPECTOR_Canvases.push(this);
}
}
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (this.parentElement || false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
})()</script><script type="text/javascript">
var spector;
var captureOnLoad = false;
var captureOffScreen = false;
window.__SPECTOR_Canvases = [];
(function() {
var __SPECTOR_Origin_EXTENSION_GetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.__SPECTOR_Origin_EXTENSION_GetContext = __SPECTOR_Origin_EXTENSION_GetContext;
if (typeof OffscreenCanvas !== 'undefined') {
var __SPECTOR_Origin_EXTENSION_OffscreenGetContext = OffscreenCanvas.prototype.getContext;
OffscreenCanvas.prototype.__SPECTOR_Origin_EXTENSION_OffscreenGetContext = __SPECTOR_Origin_EXTENSION_OffscreenGetContext;
OffscreenCanvas.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
// context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
this.id = "Offscreen";
window.__SPECTOR_Canvases.push(this);
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
}
HTMLCanvasElement.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
if (captureOffScreen) {
var found = false;
for (var i = 0; i < window.__SPECTOR_Canvases.length; i++) {
if (window.__SPECTOR_Canvases[i] === this) {
found = true;
break;
}
}
if (!found) {
window.__SPECTOR_Canvases.push(this);
}
}
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (this.parentElement || false) {
spector.captureContext(context, 500, false, false);
captureOnLoad = false;
}
}
}
return context;
}
})()</script><p>SIMD is something related to performance stuff. It makes certain things go faster (simply put). Kind of like sticking laughing gas in your petrol car's fuel line.<br /></p><p><a href="https://en.wikipedia.org/wiki/SIMD">https://en.wikipedia.org/wiki/SIMD</a></p><p>Emscripten, WebAssembly now have better support for SIMD (to various degrees)<br /></p><p><a href="https://emscripten.org/docs/porting/simd.html">https://emscripten.org/docs/porting/simd.html</a></p><p><br /></p><p>Chrome and firefox also support SIMD (to various degrees)<br /></p><p></p><p>So for Qt 6.3, I have been working to get Qt building and running using those SIMD instructions available for javascript (and thereby WebAssembly) in the web browsers (sorry, Safari.. catch up soon?)</p><p><br /></p><p>Just configure soon to be qt 6.3 with the -sse2 argument (change has not been reviewed or merged yet)</p><p><a href="https://codereview.qt-project.org/c/qt/qtbase/+/343563">https://codereview.qt-project.org/c/qt/qtbase/+/343563</a><br /></p><p>To see if it is actually worth adding SIMD support to Qt WebAssembly, I
built a couple Qt Quick benchmarks, namely the declarative particles
benchmarks - affectors and emission. <br /></p><p>I had to put image and qml files into a .qrc resource file so that Qt
WebAssembly could find them, as we have no real local file system
access.</p><p><br /></p><p>The results are much better than I expected. Clearly, there is a performance boost by using simd in wasm. </p><p> Someone else has had similar results with wasm SIMD<br /></p><p><a href="https://robaboukhalil.medium.com/webassembly-and-simd-7a7daa4f2ecd">https://robaboukhalil.medium.com/webassembly-and-simd-7a7daa4f2ecd</a><br /></p><p> </p><p>Next I want to expand the number and type of benchmarks, but this gives us early baseline results.</p><p>Chrome browser<br />no SIMD:<br /><br />********* Start testing of tst_affectors *********<br />Config: Using QtTest library 6.2.0, Qt 6.2.0 (wasm-little_endian-ilp32 static debug build; by Clang 13.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project 5852582532b3eb3ea8da51a1e272d8d017bd36c9)), unknown unknown<br />PASS : tst_affectors::initTestCase()<br />Heap resize call from 16777216 to 20185088 took 0.09999999403953552 msecs. Success: true<br />Heap resize call from 20185088 to 24248320 took 0.10000000894069672 msecs. Success: true<br />Heap resize call from 24248320 to 29097984 took 0.10000000894069672 msecs. Success: true<br />Heap resize call from 29097984 to 34930688 took 0.5 msecs. Success: true<br />PASS : tst_affectors::test_basic(16ms)<br />RESULT : tst_affectors::test_basic():"16ms":<br /> 0.29 msecs per iteration (total: 75, iterations: 256)<br />Heap resize call from 34930688 to 41943040 took 0.10000000894069672 msecs. Success: true<br />PASS : tst_affectors::test_basic(32ms)<br />RESULT : tst_affectors::test_basic():"32ms":<br /> 0.41 msecs per iteration (total: 53, iterations: 128)<br />Heap resize call from 41943040 to 50331648 took 0.29999999701976776 msecs. Success: true<br />PASS : tst_affectors::test_basic(100ms)<br />RESULT : tst_affectors::test_basic():"100ms":<br /> 0.87 msecs per iteration (total: 56, iterations: 64)<br />Heap resize call from 50331648 to 60424192 took 0.3999999910593033 msecs. Success: true<br />PASS : tst_affectors::test_basic(500ms)<br />RESULT : tst_affectors::test_basic():"500ms":<br /> 3.3 msecs per iteration (total: 53, iterations: 16)<br />Heap resize call from 60424192 to 72548352 took 0.19999998807907104 msecs. Success: true<br />PASS : tst_affectors::test_filtered(16ms)<br />RESULT : tst_affectors::test_filtered():"16ms":<br /> 0.84 msecs per iteration (total: 54, iterations: 64)<br />PASS : tst_affectors::test_filtered(32ms)<br />RESULT : tst_affectors::test_filtered():"32ms":<br /> 0.96 msecs per iteration (total: 62, iterations: 64)<br />Heap resize call from 72548352 to 87097344 took 0.20000000298023224 msecs. Success: true<br />PASS : tst_affectors::test_filtered(100ms)<br />RESULT : tst_affectors::test_filtered():"100ms":<br /> 1.3 msecs per iteration (total: 89, iterations: 64)<br />PASS : tst_affectors::test_filtered(500ms)<br />RESULT : tst_affectors::test_filtered():"500ms":<br /> 3.7 msecs per iteration (total: 60, iterations: 16)<br />PASS : tst_affectors::cleanupTestCase()<br />Totals: 10 passed, 0 failed, 0 skipped, 0 blacklisted, 15037ms<br />********* Finished testing of tst_affectors *********<br /><br />********* Start testing of tst_emission *********<br />Config: Using QtTest library 6.2.0, Qt 6.2.0 (wasm-little_endian-ilp32 static debug build; by Clang 13.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project 5852582532b3eb3ea8da51a1e272d8d017bd36c9)), unknown unknown<br />PASS : tst_emission::initTestCase()<br />Heap resize call from 16777216 to 20185088 took 0 msecs. Success: true<br />Heap resize call from 20185088 to 24248320 took 0 msecs. Success: true<br />Heap resize call from 24248320 to 29097984 took 0 msecs. Success: true<br />PASS : tst_emission::test_basic(16ms)<br />RESULT : tst_emission::test_basic():"16ms":<br /> 1.6 msecs per iteration (total: 53, iterations: 32)<br />Heap resize call from 29097984 to 34930688 took 0.4000000059604645 msecs. Success: true<br />PASS : tst_emission::test_basic(32ms)<br />RESULT : tst_emission::test_basic():"32ms":<br /> 3.1 msecs per iteration (total: 51, iterations: 16)<br />PASS : tst_emission::test_basic(100ms)<br />RESULT : tst_emission::test_basic():"100ms":<br /> 4.5 msecs per iteration (total: 73, iterations: 16)<br />PASS : tst_emission::test_basic(500ms)<br />RESULT : tst_emission::test_basic():"500ms":<br /> 21 msecs per iteration (total: 87, iterations: 4)<br />Heap resize call from 34930688 to 41943040 took 0.09999999403953552 msecs. Success: true<br />PASS : tst_emission::test_basic(1000ms)<br />RESULT : tst_emission::test_basic():"1000ms":<br /> 22 msecs per iteration (total: 89, iterations: 4)<br />PASS : tst_emission::test_basic(10000ms)<br />RESULT : tst_emission::test_basic():"10000ms":<br /> 23 msecs per iteration (total: 92, iterations: 4)<br />PASS : tst_emission::cleanupTestCase()<br />Totals: 8 passed, 0 failed, 0 skipped, 0 blacklisted, 5398ms<br />********* Finished testing of tst_emission *********<br /><br />======================================================================<br />======================================================================<br /><br />chrome SIMD</p><p><br />********* Start testing of tst_affectors *********<br />Config: Using QtTest library 6.2.0, Qt 6.2.0 (wasm-little_endian-ilp32 static debug build; by Clang 13.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project 5852582532b3eb3ea8da51a1e272d8d017bd36c9)), unknown unknown<br />PASS : tst_affectors::initTestCase()<br />Heap resize call from 16777216 to 20185088 took 0 msecs. Success: true<br />Heap resize call from 20185088 to 24248320 took 0 msecs. Success: true<br />Heap resize call from 24248320 to 29097984 took 0 msecs. Success: true<br />Heap resize call from 29097984 to 34930688 took 0.3999999761581421 msecs. Success: true<br />Heap resize call from 34930688 to 41943040 took 0.19999998807907104 msecs. Success: true<br />PASS : tst_affectors::test_basic(16ms)<br />RESULT : tst_affectors::test_basic():"16ms":<br /> 0.059 msecs per iteration (total: 61, iterations: 1024)<br />Heap resize call from 41943040 to 50331648 took 0.30000001192092896 msecs. Success: true<br />PASS : tst_affectors::test_basic(32ms)<br />RESULT : tst_affectors::test_basic():"32ms":<br /> 0.11 msecs per iteration (total: 59, iterations: 512)<br />Heap resize call from 50331648 to 60424192 took 0.30000001192092896 msecs. Success: true<br />PASS : tst_affectors::test_basic(100ms)<br />RESULT : tst_affectors::test_basic():"100ms":<br /> 0.15 msecs per iteration (total: 81, iterations: 512)<br />Heap resize call from 60424192 to 72548352 took 0.30000001192092896 msecs. Success: true<br />PASS : tst_affectors::test_basic(500ms)<br />RESULT : tst_affectors::test_basic():"500ms":<br /> 0.58 msecs per iteration (total: 75, iterations: 128)<br />Heap resize call from 72548352 to 87097344 took 0.3999999761581421 msecs. Success: true<br />PASS : tst_affectors::test_filtered(16ms)<br />RESULT : tst_affectors::test_filtered():"16ms":<br /> 0.10 msecs per iteration (total: 52, iterations: 512)<br />Heap resize call from 87097344 to 104529920 took 0.30000001192092896 msecs. Success: true<br />PASS : tst_affectors::test_filtered(32ms)<br />RESULT : tst_affectors::test_filtered():"32ms":<br /> 0.12 msecs per iteration (total: 64, iterations: 512)<br />PASS : tst_affectors::test_filtered(100ms)<br />RESULT : tst_affectors::test_filtered():"100ms":<br /> 0.19 msecs per iteration (total: 51, iterations: 256)<br />Heap resize call from 104529920 to 125435904 took 0.20000001788139343 msecs. Success: true<br />PASS : tst_affectors::test_filtered(500ms)<br />RESULT : tst_affectors::test_filtered():"500ms":<br /> 0.61 msecs per iteration (total: 79, iterations: 128)<br />PASS : tst_affectors::cleanupTestCase()<br />Totals: 10 passed, 0 failed, 0 skipped, 0 blacklisted, 9728ms<br />********* Finished testing of tst_affectors *********<br /><br />********* Start testing of tst_emission *********<br />Config: Using QtTest library 6.2.0, Qt 6.2.0 (wasm-little_endian-ilp32 static debug build; by Clang 13.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project 5852582532b3eb3ea8da51a1e272d8d017bd36c9)), unknown unknown<br />PASS : tst_emission::initTestCase()<br />Heap resize call from 16777216 to 20185088 took 0 msecs. Success: true<br />Heap resize call from 20185088 to 24248320 took 0 msecs. Success: true<br />Heap resize call from 24248320 to 29097984 took 0 msecs. Success: true<br />Heap resize call from 29097984 to 34930688 took 0.29999998211860657 msecs. Success: true<br />PASS : tst_emission::test_basic(16ms)<br />RESULT : tst_emission::test_basic():"16ms":<br /> 0.046 msecs per iteration (total: 95, iterations: 2048)<br />Heap resize call from 34930688 to 41943040 took 0 msecs. Success: true<br />PASS : tst_emission::test_basic(32ms)<br />RESULT : tst_emission::test_basic():"32ms":<br /> 0.090 msecs per iteration (total: 93, iterations: 1024)<br />Heap resize call from 41943040 to 50331648 took 0.29999998211860657 msecs. Success: true<br />PASS : tst_emission::test_basic(100ms)<br />RESULT : tst_emission::test_basic():"100ms":<br /> 0.27 msecs per iteration (total: 70, iterations: 256)<br />Heap resize call from 50331648 to 60424192 took 0.4000000059604645 msecs. Success: true<br />PASS : tst_emission::test_basic(500ms)<br />RESULT : tst_emission::test_basic():"500ms":<br /> 1.3 msecs per iteration (total: 85, iterations: 64)<br />Heap resize call from 60424192 to 72548352 took 0.4000000059604645 msecs. Success: true<br />PASS : tst_emission::test_basic(1000ms)<br />RESULT : tst_emission::test_basic():"1000ms":<br /> 1.3 msecs per iteration (total: 87, iterations: 64)<br />PASS : tst_emission::test_basic(10000ms)<br />RESULT : tst_emission::test_basic():"10000ms":<br /> 1.3 msecs per iteration (total: 86, iterations: 64)<br />PASS : tst_emission::cleanupTestCase()<br />Totals: 8 passed, 0 failed, 0 skipped, 0 blacklisted, 6017ms<br />********* Finished testing of tst_emission *********</p><p><br /></p><p>Firefox has similar results:</p><p><br /></p><p><span><span><span><span><span title="View source in Debugger → https://192.168.1.137/wasm/particles-simd/affectors/qtloader.js:372:25"><span>firefox nosimd:<br clear="none" /><br clear="none" /> ********* Start testing of tst_affectors *********<br clear="none" />
Config: Using QtTest library 6.2.0, Qt 6.2.0 (wasm-little_endian-ilp32
static debug build; by Clang 13.0.0
(/opt/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project
5f3c99085d4c2ebf57fd0586b013b02e32a8e20b)), unknown unknown<br clear="none" /> PASS : tst_affectors::initTestCase()<br clear="none" /> Heap resize call from 16777216 to 20185088 took 0 msecs. Success: true qtloader.js line 443 > eval:11829:17<br clear="none" /> Heap resize call from 20185088 to 24248320 took 0 msecs. Success: true qtloader.js line 443 > eval:11829:17<br clear="none" /> Heap resize call from 24248320 to 29097984 took 0 msecs. Success: true qtloader.js line 443 > eval:11829:17<br clear="none" /> PASS : tst_affectors::test_basic(16ms)<br clear="none" /> RESULT : tst_affectors::test_basic():"16ms":<br clear="none" /> 0.81 msecs per iteration (total: 52, iterations: 64)<br clear="none" /> PASS : tst_affectors::test_basic(32ms)<br clear="none" /> RESULT : tst_affectors::test_basic():"32ms":<br clear="none" /> 1.1 msecs per iteration (total: 76, iterations: 64)<br clear="none" /> PASS : tst_affectors::test_basic(100ms)<br clear="none" /> RESULT : tst_affectors::test_basic():"100ms":<br clear="none" /> 2.4 msecs per iteration (total: 79, iterations: 32)<br clear="none" /> Heap resize call from 29097984 to 34930688 took 17 msecs. Success: true qtloader.js line 443 > eval:11829:17<br clear="none" /> PASS : tst_affectors::test_basic(500ms)<br clear="none" /> RESULT : tst_affectors::test_basic():"500ms":<br clear="none" /> 9.1 msecs per iteration (total: 73, iterations: 8)<br clear="none" /> PASS : tst_affectors::test_filtered(16ms)<br clear="none" /> RESULT : tst_affectors::test_filtered():"16ms":<br clear="none" /> 2.3 msecs per iteration (total: 74, iterations: 32)<br clear="none" /> PASS : tst_affectors::test_filtered(32ms)<br clear="none" /> RESULT : tst_affectors::test_filtered():"32ms":<br clear="none" /> 2.6 msecs per iteration (total: 86, iterations: 32)<br clear="none" /> PASS : tst_affectors::test_filtered(100ms)<br clear="none" /> RESULT : tst_affectors::test_filtered():"100ms":<br clear="none" /> 3.8 msecs per iteration (total: 62, iterations: 16)<br clear="none" /> Heap resize call from 34930688 to 41943040 took 0 msecs. Success: true qtloader.js line 443 > eval:11829:17<br clear="none" /> PASS : tst_affectors::test_filtered(500ms)<br clear="none" /> RESULT : tst_affectors::test_filtered():"500ms":<br clear="none" /> 11 msecs per iteration (total: 88, iterations: 8)<br clear="none" /> PASS : tst_affectors::cleanupTestCase()<br clear="none" /> Totals: 10 passed, 0 failed, 0 skipped, 0 blacklisted, 16781ms<br clear="none" /> ********* Finished testing of tst_affectors ********* <br clear="none" /> <br clear="none" /> ********* Start testing of tst_emission *********<br clear="none" />
Config: Using QtTest library 6.2.0, Qt 6.2.0
(wasm-little_endian-ilp32 static debug build; by Clang 13.0.0
(/opt/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project
5f3c99085d4c2ebf57fd0586b013b02e32a8e20b)), unknown unknown<br clear="none" /> PASS : tst_emission::initTestCase()<br clear="none" /> Heap resize call from 16777216 to 20185088 took 0 msecs. Success: true qtloader.js line 443 > eval:11829:17<br clear="none" /> Heap resize call from 20185088 to 24248320 took 0 msecs. Success: true qtloader.js line 443 > eval:11829:17<br clear="none" /> Heap resize call from 24248320 to 29097984 took 0 msecs. Success: true qtloader.js line 443 > eval:11829:17<br clear="none" /> PASS : tst_emission::test_basic(16ms)<br clear="none" /> RESULT : tst_emission::test_basic():"16ms":<br clear="none" /> 2.1 msecs per iteration (total: 70, iterations: 32)<br clear="none" /> PASS : tst_emission::test_basic(32ms)<br clear="none" /> RESULT : tst_emission::test_basic():"32ms":<br clear="none" /> 4.1 msecs per iteration (total: 67, iterations: 16)<br clear="none" /> PASS : tst_emission::test_basic(100ms)<br clear="none" /> RESULT : tst_emission::test_basic():"100ms":<br clear="none" /> 8.1 msecs per iteration (total: 65, iterations: 8)<br clear="none" /> PASS : tst_emission::test_basic(500ms)<br clear="none" /> RESULT : tst_emission::test_basic():"500ms":<br clear="none" /> 43 msecs per iteration (total: 87, iterations: 2)<br clear="none" /> PASS : tst_emission::test_basic(1000ms)<br clear="none" /> RESULT : tst_emission::test_basic():"1000ms":<br clear="none" /> 43 msecs per iteration (total: 86, iterations: 2)<br clear="none" /> PASS : tst_emission::test_basic(10000ms)<br clear="none" /> RESULT : tst_emission::test_basic():"10000ms":<br clear="none" /> 43 msecs per iteration (total: 86, iterations: 2)<br clear="none" /> PASS : tst_emission::cleanupTestCase()<br clear="none" /> Totals: 8 passed, 0 failed, 0 skipped, 0 blacklisted, 4178ms<br clear="none" /> ********* Finished testing of tst_emission *********<br clear="none" /> </span></span></span></span></span></span><span><span><span><span><span title="View source in Debugger → https://192.168.1.137/wasm/particles-simd/affectors/qtloader.js:372:25"><span>======================================================================<br />======================================================================<br /> <br clear="none" /> <br clear="none" />firefox SIMD:</span></span></span></span></span></span></p><p><span><span><span><span><span title="View source in Debugger → https://192.168.1.137/wasm/particles-simd/affectors/qtloader.js:372:25"><span><br clear="none" /> ********* Start testing of tst_affectors *********<br clear="none" />
Config: Using QtTest library 6.2.0, Qt 6.2.0 (wasm-little_endian-ilp32
static debug build; by Clang 13.0.0
(/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project
5852582532b3eb3ea8da51a1e272d8d017bd36c9)), unknown unknown<br clear="none" /> PASS : tst_affectors::initTestCase()<br clear="none" /> Heap resize call from 16777216 to 20185088 took 0 msecs. Success: true<br clear="none" /> Heap resize call from 20185088 to 24248320 took 0 msecs. Success: true<br clear="none" /> Heap resize call from 24248320 to 29097984 took 0 msecs. Success: true<br clear="none" /> Heap resize call from 29097984 to 34930688 took 0 msecs. Success: true<br clear="none" /> PASS : tst_affectors::test_basic(16ms)<br clear="none" /> RESULT : tst_affectors::test_basic():"16ms":<br clear="none" /> 0.14 msecs per iteration (total: 73, iterations: 512)<br clear="none" /> Heap resize call from 34930688 to 41943040 took 0 msecs. Success: true<br clear="none" /> Heap resize call from 41943040 to 50331648 took 1 msecs. Success: true<br clear="none" /> PASS : tst_affectors::test_basic(32ms)<br clear="none" /> RESULT : tst_affectors::test_basic():"32ms":<br clear="none" /> 0.21 msecs per iteration (total: 54, iterations: 256)<br clear="none" /> Heap resize call from 50331648 to 60424192 took 5 msecs. Success: true<br clear="none" /> PASS : tst_affectors::test_basic(100ms)<br clear="none" /> RESULT : tst_affectors::test_basic():"100ms":<br clear="none" /> 0.45 msecs per iteration (total: 58, iterations: 128)<br clear="none" /> PASS : tst_affectors::test_basic(500ms)<br clear="none" /> RESULT : tst_affectors::test_basic():"500ms":<br clear="none" /> 1.8 msecs per iteration (total: 60, iterations: 32)<br clear="none" /> Heap resize call from 60424192 to 72548352 took 7 msecs. Success: true<br clear="none" /> PASS : tst_affectors::test_filtered(16ms)<br clear="none" /> RESULT : tst_affectors::test_filtered():"16ms":<br clear="none" /> 0.28 msecs per iteration (total: 73, iterations: 256)<br clear="none" /> Heap resize call from 72548352 to 87097344 took 0 msecs. Success: true<br clear="none" /> PASS : tst_affectors::test_filtered(32ms)<br clear="none" /> RESULT : tst_affectors::test_filtered():"32ms":<br clear="none" /> 0.35 msecs per iteration (total: 90, iterations: 256)<br clear="none" /> PASS : tst_affectors::test_filtered(100ms)<br clear="none" /> RESULT : tst_affectors::test_filtered():"100ms":<br clear="none" /> 0.60 msecs per iteration (total: 77, iterations: 128)<br clear="none" /> Heap resize call from 87097344 to 104529920 took 0 msecs. Success: true<br clear="none" /> PASS : tst_affectors::test_filtered(500ms)<br clear="none" /> RESULT : tst_affectors::test_filtered():"500ms":<br clear="none" /> 2.0 msecs per iteration (total: 66, iterations: 32)<br clear="none" /> PASS : tst_affectors::cleanupTestCase()<br clear="none" /> Totals: 10 passed, 0 failed, 0 skipped, 0 blacklisted, 6411ms<br clear="none" /> ********* Finished testing of tst_affectors *********<br clear="none" /> <br clear="none" /> ********* Start testing of tst_emission *********<br clear="none" />
Config: Using QtTest library 6.2.0, Qt 6.2.0 (wasm-little_endian-ilp32
static debug build; by Clang 13.0.0
(/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project
5852582532b3eb3ea8da51a1e272d8d017bd36c9)), unknown unknown<br clear="none" /> PASS : tst_emission::initTestCase()<br clear="none" /> Heap resize call from 16777216 to 20185088 took 0 msecs. Success: true<br clear="none" /> Heap resize call from 20185088 to 24248320 took 0 msecs. Success: true<br clear="none" /> Heap resize call from 24248320 to 29097984 took 0 msecs. Success: true<br clear="none" /> Heap resize call from 29097984 to 34930688 took 0 msecs. Success: true<br clear="none" /> PASS : tst_emission::test_basic(16ms)<br clear="none" /> RESULT : tst_emission::test_basic():"16ms":<br clear="none" /> 0.12 msecs per iteration (total: 65, iterations: 512)<br clear="none" /> PASS : tst_emission::test_basic(32ms)<br clear="none" /> RESULT : tst_emission::test_basic():"32ms":<br clear="none" /> 0.24 msecs per iteration (total: 63, iterations: 256)<br clear="none" /> Heap resize call from 34930688 to 41943040 took 0 msecs. Success: true<br clear="none" /> PASS : tst_emission::test_basic(100ms)<br clear="none" /> RESULT : tst_emission::test_basic():"100ms":<br clear="none" /> 0.75 msecs per iteration (total: 97, iterations: 128)<br clear="none" /> Heap resize call from 41943040 to 50331648 took 1 msecs. Success: true<br clear="none" /> PASS : tst_emission::test_basic(500ms)<br clear="none" /> RESULT : tst_emission::test_basic():"500ms":<br clear="none" /> 3.6 msecs per iteration (total: 58, iterations: 16)<br clear="none" /> Heap resize call from 50331648 to 60424192 took 4 msecs. Success: true<br clear="none" /> PASS : tst_emission::test_basic(1000ms)<br clear="none" /> RESULT : tst_emission::test_basic():"1000ms":<br clear="none" /> 3.6 msecs per iteration (total: 58, iterations: 16)<br clear="none" /> PASS : tst_emission::test_basic(10000ms)<br clear="none" /> RESULT : tst_emission::test_basic():"10000ms":<br clear="none" /> 3.6 msecs per iteration (total: 58, iterations: 16)<br clear="none" /> PASS : tst_emission::cleanupTestCase()<br clear="none" /> Totals: 8 passed, 0 failed, 0 skipped, 0 blacklisted, 3339ms<br clear="none" /> ********* Finished testing of tst_emission *********</span></span></span></span></span></span></p><p><br /></p><p><br /></p>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-40260457637801022762021-04-02T19:58:00.001+10:002021-04-03T14:05:19.237+10:00Qt 6 WebAssembly QtQuick3d or, NOT April fools<script type="text/javascript">
var spector;
var captureOnLoad = false;
var captureOffScreen = false;
window.__SPECTOR_Canvases = [];
(function() {
var __SPECTOR_Origin_EXTENSION_GetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.__SPECTOR_Origin_EXTENSION_GetContext = __SPECTOR_Origin_EXTENSION_GetContext;
if (typeof OffscreenCanvas !== 'undefined') {
var __SPECTOR_Origin_EXTENSION_OffscreenGetContext = OffscreenCanvas.prototype.getContext;
OffscreenCanvas.prototype.__SPECTOR_Origin_EXTENSION_OffscreenGetContext = __SPECTOR_Origin_EXTENSION_OffscreenGetContext;
OffscreenCanvas.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
// context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
this.id = "Offscreen";
window.__SPECTOR_Canvases.push(this);
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (false) {
spector.captureContext(context, 500, false);
captureOnLoad = false;
}
}
}
return context;
}
}
HTMLCanvasElement.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
if (captureOffScreen) {
var found = false;
for (var i = 0; i < window.__SPECTOR_Canvases.length; i++) {
if (window.__SPECTOR_Canvases[i] === this) {
found = true;
break;
}
}
if (!found) {
window.__SPECTOR_Canvases.push(this);
}
}
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (this.parentElement || false) {
spector.captureContext(context, 500, false);
captureOnLoad = false;
}
}
}
return context;
}
})()</script><script type="text/javascript">
var spector;
var captureOnLoad = false;
var captureOffScreen = false;
window.__SPECTOR_Canvases = [];
(function() {
var __SPECTOR_Origin_EXTENSION_GetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.__SPECTOR_Origin_EXTENSION_GetContext = __SPECTOR_Origin_EXTENSION_GetContext;
if (typeof OffscreenCanvas !== 'undefined') {
var __SPECTOR_Origin_EXTENSION_OffscreenGetContext = OffscreenCanvas.prototype.getContext;
OffscreenCanvas.prototype.__SPECTOR_Origin_EXTENSION_OffscreenGetContext = __SPECTOR_Origin_EXTENSION_OffscreenGetContext;
OffscreenCanvas.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
// context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
this.id = "Offscreen";
window.__SPECTOR_Canvases.push(this);
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (false) {
spector.captureContext(context, 500, false);
captureOnLoad = false;
}
}
}
return context;
}
}
HTMLCanvasElement.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
if (captureOffScreen) {
var found = false;
for (var i = 0; i < window.__SPECTOR_Canvases.length; i++) {
if (window.__SPECTOR_Canvases[i] === this) {
found = true;
break;
}
}
if (!found) {
window.__SPECTOR_Canvases.push(this);
}
}
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (this.parentElement || false) {
spector.captureContext(context, 500, false);
captureOnLoad = false;
}
}
}
return context;
}
})()</script><script type="text/javascript">
var spector;
var captureOnLoad = false;
var captureOffScreen = false;
window.__SPECTOR_Canvases = [];
(function() {
var __SPECTOR_Origin_EXTENSION_GetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.__SPECTOR_Origin_EXTENSION_GetContext = __SPECTOR_Origin_EXTENSION_GetContext;
if (typeof OffscreenCanvas !== 'undefined') {
var __SPECTOR_Origin_EXTENSION_OffscreenGetContext = OffscreenCanvas.prototype.getContext;
OffscreenCanvas.prototype.__SPECTOR_Origin_EXTENSION_OffscreenGetContext = __SPECTOR_Origin_EXTENSION_OffscreenGetContext;
OffscreenCanvas.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
// context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
this.id = "Offscreen";
window.__SPECTOR_Canvases.push(this);
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (false) {
spector.captureContext(context, 500, false);
captureOnLoad = false;
}
}
}
return context;
}
}
HTMLCanvasElement.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
if (captureOffScreen) {
var found = false;
for (var i = 0; i < window.__SPECTOR_Canvases.length; i++) {
if (window.__SPECTOR_Canvases[i] === this) {
found = true;
break;
}
}
if (!found) {
window.__SPECTOR_Canvases.push(this);
}
}
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (this.parentElement || false) {
spector.captureContext(context, 500, false);
captureOnLoad = false;
}
}
}
return context;
}
})()</script><p> I am so happy right now! As of sha 4972fdb350fe79e18b0413e74028cd9b9803f96b (1 April), you can build Qt 6 for WebAssembly!</p><p>Not only that, because QtQuick3d in Qt 6 now supports OpenGL ES2/3, it will run in a web browser!</p><p>Here is a video of the helloquick3d example, running in Firefox: [edit] running at 62 fps<br /></p><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='614' height='511' src='https://www.blogger.com/video.g?token=AD6v5dwQBLBo1FMXNWtS6TyQB8QFQhW2S_5YESKUzFxNDiH8XIcie40cI7gXdVLPzIE2DfEm1SitJ4mucUC8NpyZfQ' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div> </div><div class="separator" style="clear: both; text-align: center;"> </div><div class="separator" style="clear: both; text-align: center;">if that does not work, try this link:</div><div class="separator" style="clear: both; text-align: center;"><a href="https://vimeo.com/manage/videos/532206713">https://vimeo.com/manage/videos/532206713</a></div><div class="separator" style="clear: both; text-align: center;"> <br /></div><div class="separator" style="clear: both; text-align: center;">Now I can get on with my life and stop working on build system stuff!<br /></div><p></p>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-19035953850066456292021-01-22T19:09:00.001+10:002021-01-22T19:09:33.984+10:00Qt 6 WebAssembly<p>ahhh well now. We all know that in Qt6, qmake was ditched for cmake for the build system of Qt itself, and we are playing catch-up in the WebAssembly platform.</p><p><span style="font-size: medium;"><b>History</b></span></p><p>First a little history of the Qt build system.</p><p><a href="http://tmake.sourceforge.net/" target="_blank">tmake</a> was a perl script that generates Makefiles. If I recall correctly, it was originally written by Sam Magnuson. (I am sure someone who started in Trolltech before me will correct me if I am wrong). I met Sam when he was working at Trolltech's Brisbane office when I was hired as Qtopia Community Liasion. (Briefly, as he soon moved back to the US, after he sold me some furnature and gave me his cat! Still have the Ikea chair.)</p><p>qmake was tmake re-written in c++, which was started around 2000. (OMG - its 21 years old! - so old, it farts dust!)</p><p>qmake was added on, hacked up to tackle all kinds of things, which brings us to Qt 6.</p><p>cmake: The behemoth.</p><p>Knowing next to nothing about cmake made this journey a bit bumpy. Add in all the stuff that Qt implements in cmake and on top of that, the special arguments we use for WebAssembly means I am glad this patch is about finished!</p><p>Some things are not yet working, such as using cmake to build Qt WebAssembly applications, luckily qmake for app builds is still there... for now.</p><p>First off, until this change gets integrated into the git repo, you can grab it here:</p><p><a href="https://codereview.qt-project.org/c/qt/qtbase/+/313243">https://codereview.qt-project.org/c/qt/qtbase/+/313243</a></p><p>You will need:</p><p></p><ul style="text-align: left;"><li>Emscripten version 2.0.12 (others in the 2.0.x range should also work)</li><li>Ninja build tool (optional but highly recommended)</li><li>cmake (3.19 at least, I think)</li></ul><p></p><p><span style="font-size: medium;"><b>Host build</b></span></p><p>One of the differences between Qt5 and Qt6 is that you will need a host build with the same versioning, since Qt WebAssembly is a cross platform build. In the near future, you will be able to install Qt binary release and use that, but for now, you need to build your host Qt yourself (to get the same version as the git repo).</p><p>You need a host build of both QtBase and QtDeclarative if you are to use declarative in your Qt WebAssembly apps.</p><p><span style="font-size: medium;"><b>WebAssembly build</b></span></p><p>On all platforms, you will need to set <span style="font-size: small;">CMAKE_TOOLCHAIN_FILE and </span><span style="font-size: small;">QT_HOST_PATH. Emscripten comes with a convient cmake toolchain file, so set the </span><span style="font-size: small;">CMAKE_TOOLCHAIN_FILE to where ever it is installed. </span><span style="font-size: small;">QT_HOST_PATH is set to where the Qt 6 host directory is.</span></p><p><b>Windows:</b></p><p>Just like with Qt5, you will need the Mingw toolchain installed.</p><p>On windows, we can use the mingw toolchain that gets installed with Qt binaries, so make sure mingw32-make is in your PATH. I usually do this after I run emsdk_env.bat to set up the Emscripten toolchain. To configure Qt, I use something like:</p><p><span style="font-family: inherit; font-size: x-small;">cmake -DCMAKE_GENERATOR=Ninja -DCMAKE_TOOLCHAIN_FILE=H:\development\emsdk\upstream\emscripten\cmake\Modules\Platform\Emscripten.cmake -DFEATURE_developer_build=ON -DFEATURE_headersclean=OFF -DWARNINGS_ARE_ERRORS=OFF -DQT_BUILD_EXAMPLES=OFF -DQT_BUILD_TESTS=OFF -DQT_HOST_PATH=H:\development\platforms\desktop\qtbase H:\development\depot\qt\qt5\qtbase</span></p><div><br /></div><div><b>Linux/Mac:</b></div><div><br /></div><div>You will need gcc on Linux and Xcode 10.15 or greater on Mac.</div><div><br /></div><div><div><span style="font-size: x-small;">cmake -DFEATURE_developer_build=ON</span></div><div><span style="font-size: x-small;">-DFEATURE_headersclean=OFF</span></div><div><span style="font-size: x-small;">-DWARNINGS_ARE_ERRORS=OFF</span></div><div><span style="font-size: x-small;">-DQT_BUILD_EXAMPLES=OFF</span></div><div><span style="font-size: x-small;">-DQT_BUILD_TESTS=OFF</span></div><div><span style="font-size: x-small;">-DCMAKE_GENERATOR=Ninja</span></div><div><span style="font-size: x-small;">-DQT_HOST_PATH=/development/platforms/desktop/qtbase</span></div><div><span style="font-size: x-small;">-DCMAKE_TOOLCHAIN_FILE=/emsdk/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake</span></div><div><span style="font-size: x-small;">/depot/qt/qt5/qtbase</span></div></div><div><br /></div><div><div>There are a couple <i>experimental</i> features you can configure Qt for, such as</div><div><ul><li>threads: -DFEATURE_threads=On</li></ul></div></div><div>I have probably forgotten something important, but this will get someone started that is interested. I have tested this build on Linux, Mac and Windows.</div><div><br /></div><div><span style="font-size: medium;"><b>QtDeclarative:</b></span></div><div><br /></div><div>To configure declarative using cmake, it's fairly straight forward. In the qtbase/bin directory is a helper tool:</div><div>qt-configure-module ~/depot/qt/qt5/qtdeclarative</div><div><br /></div><div><div><br /></div></div><div><span style="font-size: medium;"><b>Application builds:</b></span></div><div><span style="font-size: medium;"><b><br /></b></span></div><div>For now, you can use qmake in the old fashioned way to configure applications.</div><div><br /></div><div>See also the Qt WebAssembly wiki: <a href="https://wiki.qt.io/Qt_for_WebAssembly">https://wiki.qt.io/Qt_for_WebAssembly</a></div><div><br /></div>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com1tag:blogger.com,1999:blog-2997739907109253354.post-21229087439350396572020-07-22T10:04:00.000+10:002020-07-22T10:04:29.169+10:00'wasm memory too small' Qt for WebAssembly<script type="text/javascript">
var spector;
var captureOnLoad = false;
var captureOffScreen = false;
window.__SPECTOR_Canvases = [];
(function() {
var __SPECTOR_Origin_EXTENSION_GetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.__SPECTOR_Origin_EXTENSION_GetContext = __SPECTOR_Origin_EXTENSION_GetContext;
if (typeof OffscreenCanvas !== 'undefined') {
var __SPECTOR_Origin_EXTENSION_OffscreenGetContext = OffscreenCanvas.prototype.getContext;
OffscreenCanvas.prototype.__SPECTOR_Origin_EXTENSION_OffscreenGetContext = __SPECTOR_Origin_EXTENSION_OffscreenGetContext;
OffscreenCanvas.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
// context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
this.id = "Offscreen";
window.__SPECTOR_Canvases.push(this);
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (false) {
spector.captureContext(context, 500, false);
captureOnLoad = false;
}
}
}
return context;
}
}
HTMLCanvasElement.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
if (captureOffScreen) {
var found = false;
for (var i = 0; i < window.__SPECTOR_Canvases.length; i++) {
if (window.__SPECTOR_Canvases[i] === this) {
found = true;
break;
}
}
if (!found) {
window.__SPECTOR_Canvases.push(this);
}
}
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (this.parentElement || false) {
spector.captureContext(context, 500, false);
captureOnLoad = false;
}
}
}
return context;
}
})()</script><div><script type="text/javascript">
var spector;
var captureOnLoad = false;
var captureOffScreen = false;
window.__SPECTOR_Canvases = [];
(function() {
var __SPECTOR_Origin_EXTENSION_GetContext = HTMLCanvasElement.prototype.getContext;
HTMLCanvasElement.prototype.__SPECTOR_Origin_EXTENSION_GetContext = __SPECTOR_Origin_EXTENSION_GetContext;
if (typeof OffscreenCanvas !== 'undefined') {
var __SPECTOR_Origin_EXTENSION_OffscreenGetContext = OffscreenCanvas.prototype.getContext;
OffscreenCanvas.prototype.__SPECTOR_Origin_EXTENSION_OffscreenGetContext = __SPECTOR_Origin_EXTENSION_OffscreenGetContext;
OffscreenCanvas.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_OffscreenGetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
// context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
this.id = "Offscreen";
window.__SPECTOR_Canvases.push(this);
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (false) {
spector.captureContext(context, 500, false);
captureOnLoad = false;
}
}
}
return context;
}
}
HTMLCanvasElement.prototype.getContext = function () {
var context = null;
if (!arguments.length) {
return context;
}
if (arguments.length === 1) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0]);
if (context === null) {
return context;
}
}
else if (arguments.length === 2) {
context = this.__SPECTOR_Origin_EXTENSION_GetContext(arguments[0], arguments[1]);
if (context === null) {
return context;
}
}
var contextNames = ["webgl", "experimental-webgl", "webgl2", "experimental-webgl2"];
if (contextNames.indexOf(arguments[0]) !== -1) {
context.canvas.setAttribute("__spector_context_type", arguments[0]);
// Notify the page a canvas is available.
var myEvent = new CustomEvent("SpectorWebGLCanvasAvailableEvent");
document.dispatchEvent(myEvent);
if (captureOffScreen) {
var found = false;
for (var i = 0; i < window.__SPECTOR_Canvases.length; i++) {
if (window.__SPECTOR_Canvases[i] === this) {
found = true;
break;
}
}
if (!found) {
window.__SPECTOR_Canvases.push(this);
}
}
if (captureOnLoad) {
// Ensures canvas is in the dom to capture the one we are currently tracking.
if (this.parentElement || false) {
spector.captureContext(context, 500, false);
captureOnLoad = false;
}
}
}
return context;
}
})()</script>Sometimes when I am building a larger project with Qt for WebAssembly, I get this type of message:</div><div><br /></div><div><code>wasm-ld: error: initial memory too small, 17553264 bytes needed</code></div><div><br /></div><div>and the build fails.</div><div><br /></div><div>This means that you need to tell Emscripten compiler to allocate more than the standard 1GB initial memory.</div><div>Qt allows you to specify to add more initial memory by using QMAKE_TOTAL_MEMORY in your pro file.</div><div><br /></div><div>So it makes sense to add something like this:</div><div><br /></div><div>QMAKE_TOTAL_MEMORY=17553264</div><div><br /></div><div>BUT the result is:</div><div><br /></div><div><code>shared:ERROR: For wasm, TOTAL_MEMORY must be a multiple of 64KB, was 17553264</code></div><div><br /></div><div>grrrr... ok, what if we find a multiple of 64?</div><div><br /></div><div>17553264 / 64 = 274,269.95</div><div><br /></div><div>We need a whole number, so lets round up to the next whole number.</div><div><br /> </div><div>274270 * 64 = 17553280</div><div><br /></div><div>But no. That doesn't work either:</div><div><br /></div><div><code>shared:ERROR: For wasm, TOTAL_MEMORY must be a multiple of 64KB, was 17553280</code></div><div><br /></div><div>WTH?!?</div><div><br /></div><div>The answer is that a KB is 1024 bytes, so 64 * 1024 = 65536 bytes<br /></div><div><br /></div><div>So let's find the closest whole number multiple of 64 KB (65536)</div><div><br /></div><div>17553264 / 65536 = 267.841...</div><div><br /></div><div>so let's try 268</div><div><br /></div><div>268 * 65536 = 17563648</div><div><br /></div><div><code>QMAKE_TOTAL_MEMORY=17563648</code></div><div><br /></div><div>and this now builds, and hopefully runs!</div><div><br /></div><div><br /></div><div><dl><dt class="attachment-title"><div><span style="font-family: "helvetica";">You can also read more about Mobile and Embedded development and Qt for WebAssembly in the book </span><a href="http://bit.ly/HandsOnMobileEmbedded" style="font-family: "helvetica";" target="_blank">Hands-On Mobile and Embedded Development with Qt 5</a></div></dt><dd><br /></dd></dl></div>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com1tag:blogger.com,1999:blog-2997739907109253354.post-27481233863555846292020-05-29T20:00:00.000+10:002020-05-29T20:00:13.558+10:00Debugging Qt for WebAssembly<br />
Debugging is often difficult in the best of times. Debugging Qt webassembly apps in the web browser is not something I want to do on a nice summer day, and certainly makes a person more appreciative of mature, gui based debugging. But sometimes you have to do what you have to do.<br />
<br />
If you have a crash on your Qt for WebAssembly app, you would see a not very human readable backtrace in the console output. You can start by recompiling your app with CONFIG+=debug, which allows nice symbol names in the backtrace that appears in the dev console of the browser when an exception or crash happens. Many times this can lead you to find your bug fairly quickly.<br />
<br />
NOTE: The Qt libraries do not need to be compiled in debug mode for you to just see symbols while debugging. Although, if you want to step through C++ source code using sourcemaps, you will need to compile Qt and your application in debug mode.<br />
<br />
The easy way is to add qDebug output, run it and see what happens. I do this quite often. But just as often, it is not enough to find a particular bug.<br /><br />
One issue with that is linking emscripten based wasm files takes a long time (because that is where all the magic happen), so this method is rather time consuming. Thankfully, with Qt 5.15, we use Emscripten 1.39.8 which is based on upstream clang, transpiles directly to wasm binaries and skips the internediate step of outputting javascript, so link time is greatly reduced.<div><br /></div><div>What if you need to step through the source code like a desktop debugger? QtCreator does not at this time, have support for debugging Qt WebAssembly apps. I suppose it could be implemented using the browsers <a href="https://chromedevtools.github.io/devtools-protocol/" target="_blank">remote debugging interface API </a><br />
<br />
Emscripten can be used to generate source maps for the browser debugger to utilize for debugging. In Qt, we use qmake to add the necessary linker lines to allow emscripten to generate these source maps. Qt should do this by default when you use <font face="trebuchet">CONFIG+=debug</font>. </div><div><br /></div><div>There is a bug that was recently fixed regarding this. If you do not find any source maps files (.map), the workaround is to add QMAKE_LFLAGS_DEBUG += -g4 in your pro file.</div><div><br />
By default, the source base in Qt is set to use http://localhost:8000/<br />
<br />
You can change this by adding into your .pro file, something like:<br />
QMAKE_WASM_SOURCE_MAP_BASE = http://myip:6931 /<br />
<br />
Emrun by default uses port 6931.<div><br /></div><div>If you are running a server of some sort, you can use the ip for that. </div><div><br /></div><div>Sources need to be in the directory tree seen by the server, so an in-source build is the easiest. In my experience, Chrome browser seems to be able to debug with sourcemaps better. If you are using Mac or Linux, you can simply symlink the source file path into the web server base.<br />
<br />
This is what happens when you do not have this set up correctly:<br />
<br />
Error while fetching an original source: request failed with status 404<br />
Source URL: http://192.168.0.21:6931/src/wasm-windows/main.cpp</div><div><br /></div><div>Or pehaps you see a message in the sources tab of the browser console such as:</div><div>Could not load content for http://localhost:8000/depot/qt/qt5/qtbase/examples/widgets/richtext/textedit/textedit.cpp (HTTP error: status code 404, net::ERR_HTTP_RESPONSE_CODE_FAILURE)</div><div><br /></div><div>It means it would not find the specific source file. You can symlink the sources directory.</div><div>I am using chrome, as I had better luck with it utilizing the sourcemaps. The actual procedure for firefox might be a bit different. It isn't working for me at this time.</div><div>
<ul style="text-align: left;">
<li>emrun --browser chrome --port 8000 --hostname localhost --serve_after_close textedit.html </li><ul><li>(or wasmsourceserver.py as below)</li></ul>
<li>open the web console</li>
<li>[chrome] Go into the debugger by clicking on 'sources'</li><li>[firefox] Uses the 'Debugger' tab</li>
</ul>
<div>
You should now see some entries to source files, including our main.cpp</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-uY0YGkYQ7rY/XtDQsPc4MjI/AAAAAAAAirg/Ml3bsuCX8rsinBvMXq7HnE4lg3ERXN4TQCK4BGAsYHg/Screen%2BShot%2B2020-05-29%2Bat%2B7.06.22%2Bpm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="960" data-original-width="2000" height="308" src="https://1.bp.blogspot.com/-uY0YGkYQ7rY/XtDQsPc4MjI/AAAAAAAAirg/Ml3bsuCX8rsinBvMXq7HnE4lg3ERXN4TQCK4BGAsYHg/w640-h308/Screen%2BShot%2B2020-05-29%2Bat%2B7.06.22%2Bpm.png" width="640" /></a></div><div><br /></div><div>You can now step in, step out, step over and even set breakpoints, just like a desktop gui debugger like QtCreator.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-auJlRtHQ9cM/XtDMcybS7qI/AAAAAAAAiq8/onRReLvXXQA5WqS4dqZ1z-pStDDgnT2sACK4BGAsYHg/Screen%2BShot%2B2020-05-29%2Bat%2B6.33.59%2Bpm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2754" data-original-width="3924" height="450" src="https://1.bp.blogspot.com/-auJlRtHQ9cM/XtDMcybS7qI/AAAAAAAAiq8/onRReLvXXQA5WqS4dqZ1z-pStDDgnT2sACK4BGAsYHg/w640-h450/Screen%2BShot%2B2020-05-29%2Bat%2B6.33.59%2Bpm.png" width="640" /></a></div><div><br /></div>
<div>
<br /></div>
<div>
Note that these file paths are relative, and will create an error when you try debugging it, when it tries to step into a source file not local. This defeats the purpose of steping into code for the most part.</div>
<div>
<br /></div>
<div>
Morten has a work around on the QTBUG <a href="https://bugreports.qt.io/browse/QTBUG-72002" target="_blank">https://bugreports.qt.io/browse/QTBUG-72002</a> with a python web server named:</div>
<div>
<dl>
<dt class="attachment-title"><a href="https://bugreports.qt.io/secure/attachment/80709/wasmsourceserver.py" target="_blank" title="Latest 06 Feb '19 6:11 PM - Morten Sørvig">wasmsourceserver.py</a></dt>
<dt class="attachment-title"><br /></dt>
<dt class="attachment-title">By running this custom server, it will build a file map of all the files in the current directly reursively.</dt><dt class="attachment-title"><br /></dt><dt class="attachment-title"><br /></dt><dt class="attachment-title"><div>Another way to add a type of breakpoint, if you want to stop execution on a certain line and popup the browser debugger programmatically, you can add</div></dt><dt class="attachment-title"><div><pre> emscripten_debugger();<span class="p"> </span></pre>on the line you want it to stop and pop up. This is in the header emscripten.h, and recompile.</div></dt><dt class="attachment-title"><div><br /></div></dt><dt class="attachment-title"><div class="separator" style="clear: both; text-align: center;"><a href="https://1.bp.blogspot.com/-uYcyRUt6aVo/XtDMWKbQXqI/AAAAAAAAiqs/LXnhIixSM5U4KEBvroYpP2McRWFtR87YgCK4BGAsYHg/Screen%2BShot%2B2020-05-29%2Bat%2B6.47.53%2Bpm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="310" data-original-width="1374" height="144" src="https://1.bp.blogspot.com/-uYcyRUt6aVo/XtDMWKbQXqI/AAAAAAAAiqs/LXnhIixSM5U4KEBvroYpP2McRWFtR87YgCK4BGAsYHg/w640-h144/Screen%2BShot%2B2020-05-29%2Bat%2B6.47.53%2Bpm.png" width="640" /></a></div><div><br /></div></dt><dt class="attachment-title"><div><br /></div></dt><dt class="attachment-title"><div><br /></div></dt><dt class="attachment-title"><div><span style="font-family: helvetica;">You can also read more about Mobile and Embedded development and Qt for WebAssembly in the book </span><a href="http://bit.ly/HandsOnMobileEmbedded" style="font-family: helvetica;" target="_blank">Hands-On Mobile and Embedded Development with Qt 5</a></div></dt><dt class="attachment-title"><div><br /></div></dt><dt class="attachment-title"><div><br /></div></dt>
</dl>
</div>
</div></div>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-43105405363568307262020-03-26T16:20:00.000+10:002020-03-26T16:20:18.838+10:00Qt on RaspberryPiQt on RaspberryPi is really easy, but can be rather time consuming to build an image to run on raspberry pi with Qt 5. You can either do it up yourself manually grabbing the sources, tool chains, etc. You could also buy the <a href="https://doc.qt.io/QtForDeviceCreation/" target="_blank">Qt for device creation</a> package which comes with great support, bells, whistles and commercial licensing, or build it yourself and use open source GPL licensing. To get started using the open sourced parts, you clone one git repo:<br />
<ul>
<li>meta-boot2qt </li>
</ul>
<code>git clone git<a href="http://code.qt.io/yocto/meta-boot2qt.git" rel="vcs-git" title="yocto/meta-boot2qt.git Git repository">://code.qt.io/yocto/meta-boot2qt.git</a></code><br />
<br />
You then need to decide which device to target. I have a fancy new raspberry pi 4 hooked up to a touch screen, so I will choose that.<br />
<br />
To get a list of the Boot2Qt targets run the command:<br />
<br />
<code>meta-boot2qt/b2qt-init-build-env list-devices</code><br />
<br />
To initialize the build environment for a raspberry pi:<br />
<br />
<code> meta-boot2qt/b2qt-init-build-env init --device raspberrypi4</code><br />
<br />
This will clone needed repo's.
<br />
<br />
You will then need to set up the environment specifically for your device by setting MACHINE<br />
and then source the setup-environment.sh script:<br />
<br />
<code> export MACHINE=raspberrypi4 </code><br />
<code> source ./setup-environment.sh</code><br />
<br />
Now, if you just want an image to run:<br />
<br />
<code> bitbake b2qt-embedded-qt5-image</code><br />
<br />
Sit back, enjoy a cuppa and a pizza on this fine day... Don't let the computer sleep, or you will kick yourself in a few hours when you check on the progress. My final image to be dd'd onto the sd card was found in<br />
<br />
<code>tmp/deploy/images/raspberrypi4</code><br />
<br />
This is what you will be presented with when you boot your device:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-VpoX0dyX3Ik/XnxIgp3NzdI/AAAAAAAAhFM/iUTvUJntoq48Y49lruXHZCaURE1pWEFGQCLcBGAsYHQ/s1600/IMG_20200326_160922299.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1200" data-original-width="1600" height="240" src="https://1.bp.blogspot.com/-VpoX0dyX3Ik/XnxIgp3NzdI/AAAAAAAAhFM/iUTvUJntoq48Y49lruXHZCaURE1pWEFGQCLcBGAsYHQ/s320/IMG_20200326_160922299.jpg" width="320" /></a></div>
<br />
To build the sdk/sysroot the command would be:<br />
<br />
<code> bitbake meta-toolchain-b2qt-embedded-qt5-sdk </code><br />
<code><br /></code>
You can build other targets including qemu, which will give you an image that runs in the qemu emulator.<br />
<br />
You can then also set up target kits in Qt Creator to target the raspberry pi, set up the device kit so you can run and debug on the device. More fun for all that time on your hands right now!<br />
<br />
I write about creating device OS images using Qt's Boot To Qt and Bitbake for the Raspeberry Pi in my book, <a href="http://bit.ly/HandsOnMobileEmbedded" target="_blank">Hands-On Mobile and Embedded Development with Qt 5</a>
<br />
<br />
<br />deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com2tag:blogger.com,1999:blog-2997739907109253354.post-20084520026278356472020-03-24T03:53:00.000+10:002020-03-24T03:53:08.236+10:00QtWebAssembly updates Emscripten RequirementIn the just released Qt 5.15-beta2 version, Qt for WebAssembly will require an Emscripten update from 1.38.27 to 1.39.8. Require because there are a few incompatible changes we needed in Qt.<br />
<br />
The update includes several improvements, including faster linking times, as Emscripten no longer has to transpile to javascript before it outputs wasm. It can build directly to wasm thanks to upstream wasm support in clang.<br />
<br />
Users will notice app build times are greatly improved on all platforms, since Emscripten no longer has a two pass linker procedure.<br />
<br />
To update Emscripten, I usually do this from the commandline:<br />
<br />
<code>
cd ~/emsdk<br />
git pull<br />
./emsdk update-tags<br />
./emsdk install 1.39.8<br />
./emsdk activate --embedded 1.39.8<br />
</code><br />
and finally<br />
<br />
<code>
source ~/emsdk/emsdk_env.sh</code><br />
<br />
<br />
Of course, you will need to rebuild Qt, all other modules and apps.<br />
<br />
You can read more about Qt for WebAssembly and Embedded Qt development in the book <a href="http://bit.ly/HandsOnMobileEmbedded" target="_blank">Hands-On Mobile and Embedded Development with Qt 5</a>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-73230729258576997882020-01-08T11:27:00.001+10:002020-01-08T11:27:33.729+10:00Qt WebAssembly faster buildsAdmittedly, building Qt WebAssembly apps takes what seems like forever. Technically, it is the linking part that takes a huge amount of time, as that is where the magic happens. <br />
<br />
You may have missed my first blog regarding faster build times for Qt WebAssembly apps:<br />
<a href="http://qtandeverything.blogspot.com/2019/06/faster-link-time-for-qt-webassembly.html">http://qtandeverything.blogspot.com/2019/06/faster-link-time-for-qt-webassembly.html</a><br />
<br />
Here is how to get set up to use this now integrated feature.<br />
<br />
1. You need emscripten with upstream clang, which has support for transpiling directly to wasm, instead of taking the intermediary step of building javascript, then outputting wasm binary with that. Starting with version 1.39.0, upstream clang is the default. You can use "latest" to get 1.39.x or something like "sdk-upstream-1.38.43-64bit" to get an earlier version.<br />
<br />
./emsdk install latest<br />
./emsdk activate --embedded latest<br />
source ./emsdk_env.sh<br />
<br />
You can then configure and rebuild Qt with <b>-device-option WASM_OBJECT_FILES=1</b><br />
<br />
configure -xplatform wasm-emscripten -developer-build -nomake tests -nomake examples -opensource -confirm-license -verbose -compile-examples -no-warnings-are-errors -release -device-option WASM_OBJECT_FILES=1<br />
<br />
Then you can build your Qt app as normal using the qmake from that build, and watch the build time speed up!<br />
<br />
*NOTE* Using WASM_OBJECT_FILES with a multi-threaded build is still experimental, as both threads and using wasm object files in emscriptem/wasm and Qt are experimental. I have not seen any real issues regarding the use of both of these at the same time.<br />
<br />
You can read more about Qt for WebAssembly, mobile and embedded development in the book <a href="http://bit.ly/HandsOnMobileEmbedded" target="_blank">Hands-On Mobile and Embedded Development with Qt 5</a>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-11019936154326735132020-01-07T09:27:00.000+10:002020-01-07T09:27:33.883+10:00Aussie firestormsAs some may know, I currently reside in Australia, which is undergoing an unprecedented bush fire season. Total burnt area is about the size of Demark, with no end in sight. Whole forests and towns are being incinerated. Millions of animals, both wild and farm are being killed and injured by the swiftly moving crown fire being whipped up by high dry winds.<br />
<br />
There are people that need help, and people that rescue animal need help as well.<br />
<a href="https://www.news.com.au/technology/environment/bushfire-relief-how-you-can-help-those-in-need/news-story/a0476ac3538b8c373f281ea6be204421">https://www.news.com.au/technology/environment/bushfire-relief-how-you-can-help-those-in-need/news-story/a0476ac3538b8c373f281ea6be204421</a><br />
<br />
Some of you may have seen that I authored a book that was published by <a href="https://www.packtpub.com/" target="_blank">Packt</a> this last year<br />
<a href="http://bit.ly/HandsOnMobileEmbedded" target="_blank">Hands on Mobile and Embedded Development with Qt 5</a><br />
<br />
I have decided the donate all my last quarters royalties to the <a href="https://www.wires.org.au/" target="_blank">WIRES</a> Emergency Fund for Wildlife. It's not much, but I wanted to help out creatures that cannot help themselves or cannot seek out help. If there is any interest, I will donate the next quarters royalties as well.<br />
<br />
<br />deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-58949618280509487212019-08-28T18:44:00.000+10:002019-08-28T18:44:17.212+10:00AsteroidOS - the open source watchSo I get a message regarding changes needed to Sensor Framework and QtSensors to add heart rate sensor support for AsteroidOS. <a href="https://asteroidos.org/" target="_blank">AsteroidOS</a> is an open source operating system for watches. It uses OpenEmbedded and Yocto to build the system image and kernel that you can flash to a <a href="https://asteroidos.org/install/" target="_blank">small group of Android watches</a>. The UI is based on Qt and Qml.<br />
<br />
I thought about it for approximately 30 seconds and I bought a used LG Urbane watch. Which is listed on the <a href="https://asteroidos.org/install/" target="_blank">Install documentation</a> for the OS, as one of the watches that can run AsteroidOS. I choose LG, as I am partial to their Smart TV's, which happen to run Qt and QML.<br />
<br />
First issue I ran into was that adb did not see the watch on WearOS, even in developer mode, but once I changed the usb cable to a good, solid cable (actually is an old Nokia cable that still works like new!), I was then able to flash the pre-built AsteroidOS 'nightly' build image on it. You might want to backup the image on the watch before you do, unlike what I did. First flash resulted in a boot loop, so I flashed the image again, and this one booted!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-vfCj-6lqdUo/XVHyZ1zhSqI/AAAAAAAAcHI/it2F8V_LZR0Sb-8Ybx6O2QP5CHHwWskXgCLcBGAs/s1600/IMG_20190812_122812395.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1200" height="320" src="https://1.bp.blogspot.com/-vfCj-6lqdUo/XVHyZ1zhSqI/AAAAAAAAcHI/it2F8V_LZR0Sb-8Ybx6O2QP5CHHwWskXgCLcBGAs/s320/IMG_20190812_122812395.jpg" width="240" /></a></div>
<br />
<br />
In order to test new sensors code, I need to<a href="https://asteroidos.org/wiki/creating-an-asteroid-app/" target="_blank"> build the thing</a>. Essentially, it was simply to git clone the repo, run a script, and build the image. Too easy! Luckily I have a fast development machine and it did not take too long to complete.<br />
Again I had to flash it two times. As long as I can get into the fastboot screen, everything is ok.<br />
<br />
WearOS has a wrist gesture sensor, which in WearOS, turns on the display when the user flicks their wrist. iOS does this as well. AsteroidOS does not yet have support for utilizing this, so that is one of the first things I would like to fix. Neither QtSensors or Sensor Framework has support for this sensor yet. Since it is open source and I am familiar with sensor API's that run on it, I can fix this on my own!<br />
<br />
You can read more about Qt development for Mobile and Embedded devices in the book <a href="http://bit.ly/HandsOnMobileEmbedded" target="_blank">Hands-On Mobile and Embedded Development with Qt 5</a><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
You can learn more about Yocto and embedded programming with Qt in my book <a href="http://bit.ly/HandsOnMobileEmbedded" target="_blank">Hands-on Mobile and Embedded Programming with Qt 5</a>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-24390895139538397612019-08-15T13:29:00.000+10:002019-08-15T13:29:49.371+10:00QMqtt & QWebAssembly<a href="https://doc.qt.io/qt-5/wasm.html" target="_blank">Qt for WebAssembly</a> is an exciting new platform for Qt. It means that Qt applications can run in a web browser. This means that deploying a Qt application is as easy as copying the files to your properly configured web server and handing out the url, for your users to run in their web browser.<br />
<br />
Mqtt is a machine to machine communication protocol, which is used for messaging in IoT. Websocket is a two way communication protocol between a web browser and a web server.<br />
<br />
<a href="https://doc.qt.io/QtMQTT/index.html" target="_blank">QtMqtt</a> has been ported to run on the WebAssembly platform, using <a href="https://doc.qt.io/qt-5/qtwebsockets-index.html" target="_blank">QtWebSockets</a> as it's transport. There is a catch to getting it working, however. That catch is that you need to use the WebSocketIODevice class that is in the QtMqtt websocketsubscription example app. WebSocketIODevice depends on the QtWebSocket module.<br />
<br />
You need to copy websocketiodevice.h and websocketiodevice.cpp into your project, using the WebSocketIODevice as QMqttClient's transport.<br />
<br />
In a nutshell, at a minimum, code such as this:<br />
<br />
QMqttClient m_client<br />
WebSocketIODevice m_device;<br />
m_device.setUrl(m_url);<br />
connect(&m_device, &WebSocketIODevice::socketConnected, this, [this]() {<br />
m_client.setTransport(&m_device, QMqttClient::IODevice);<br />
}<br />
<br />
Then you can use the QMqttClient as normal.<br />
<br />
You can read more about Qt for WebAssembly, QtMqtt and QtWebSockets in the book <a href="http://bit.ly/HandsOnMobileEmbedded" target="_blank">Hands-On Mobile and Embedded Development with Qt 5</a>deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com2tag:blogger.com,1999:blog-2997739907109253354.post-39785293293181442762019-07-02T07:38:00.001+10:002019-07-02T07:38:47.974+10:00[Update] Faster link time for Qt WebAssemblyThe guys working on Emscripten have begun integrating the new llvm wasm backend into mainstream emscripten. Although it is still not the default, it is now heaps easier to install and use, as you no longer need to build llvm yourself.<br />
<br />
<a href="https://v8.dev/blog/emscripten-llvm-wasm">https://v8.dev/blog/emscripten-llvm-wasm</a><br />
<br />
The jist of it is:<br />
<br />
emsdk install latest-upstream<br />
emsdk activate latest-upstream<br />
<br />
According to the linked blog, there are code size benefits as well as link time speed-ups.<br />
<br />
The faster link time mostly affects application builds.<br />
<br />
Of course, you will need to recompile Qt for WebAssembly to use this, and need to configure it with the Qt WebAssembly specific option:<br />
<br />
-device-option WASM_OBJECT_FILES=1<br />
<br />
After that, you just need to run qmake as normal.<br />
<br />
One note: You will need to remove the line<br />
<style type="text/css">
p, li { white-space: pre-wrap; }
</style>
<pre><!--StartFragment--><span style="color: silver;"> </span><span style="color: green;">-s</span><span style="color: silver;"> </span><span style="color: green;">\"BINARYEN_TRAP_MODE=\'clamp\'\"</span></pre>
<pre>
</pre>
<pre><div style="font-family: "Times New Roman";">
from the mkspecs/wasm-emscripten/qmake.conf, as upstream llvm webassembly backend does it's own overflow clamping and does not support BINARYEN_TRAP_MODE argument.</div>
<div>
</div>
<div>
</div>
</pre>
deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com2tag:blogger.com,1999:blog-2997739907109253354.post-26848772299722456332019-06-07T18:50:00.000+10:002019-06-07T18:50:06.739+10:00Faster link time for Qt WebAssemblyI have built Qt and various apps using emscripten so many times over the last couple of years, it isn't even funny.<br />
<br />
One detractor with building Qt applications for the web using Qt for WebAssembly, is the time it takes to build the client application. Especially during linking, it takes a huge amount of time to produce a binary for Qt WebAssembly.<br />
This is caused by all the magic that the linker in emscripten is doing.<br />
<br />
<div>
Luckily, llvm can now produce wasm binaries directly so emscripten does not have to go through any additional steps to output wasm. Emscripten and Qt can utilize this feature to reduce the length of time it takes to link and emit a .wasm binary.</div>
<div>
<br />
YAY! \0/<br />
<br /></div>
<div>
I did some very non scientific measurements on build time. Using my development machine with make -j8:</div>
<div>
<br /></div>
<table border="1" cellpadding="0" cellspacing="0" dir="ltr" style="border-collapse: collapse; border: none; font-family: arial,sans,sans-serif; font-size: 10pt; table-layout: fixed; width: 0px;" xmlns="http://www.w3.org/1999/xhtml"><colgroup><col width="386"></col><col width="471"></col><col width="361"></col></colgroup><tbody>
<tr style="height: 21px;"><td style="overflow: hidden; padding: 2px 3px 2px 3px; vertical-align: bottom;"></td><td data-sheets-value="{"1":2,"2":"Qt (not including configure):"}" style="background-color: white; font-family: "Times New Roman"; font-size: 12pt; overflow: hidden; padding: 2px 3px; vertical-align: bottom;">Qt (not including configure)</td><td data-sheets-value="{"1":2,"2":"textedit"}" style="background-color: white; font-family: "Times New Roman"; font-size: 12pt; overflow: hidden; padding: 2px 3px; vertical-align: bottom;">textedit</td></tr>
<tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"default Qt"}" style="background-color: white; font-family: "Times New Roman"; font-size: 12pt; overflow: hidden; padding: 2px 3px; vertical-align: bottom;">default configure Qt for WebAssembly</td><td data-sheets-value="{"1":2,"2":"user 20m3.706s"}" style="overflow: hidden; padding: 2px 3px 2px 3px; vertical-align: bottom;">user 20m3.706s</td><td data-sheets-value="{"1":2,"2":"user 2m46.678s"}" style="overflow: hidden; padding: 2px 3px 2px 3px; vertical-align: bottom;">user 2m46.678s</td></tr>
<tr style="height: 21px;"><td data-sheets-value="{"1":2,"2":"using -device-option WASM_OBJECT_FILES=1"}" style="background-color: white; font-family: "Times New Roman"; font-size: 12pt; overflow: hidden; padding: 2px 3px; vertical-align: bottom;">using -device-option WASM_OBJECT_FILES=1</td><td data-sheets-value="{"1":2,"2":"user 23m30.230s"}" style="overflow: hidden; padding: 2px 3px 2px 3px; vertical-align: bottom;">user 23m30.230s</td><td data-sheets-value="{"1":2,"2":"user 0m29.435s"}" style="overflow: hidden; padding: 2px 3px 2px 3px; vertical-align: bottom;">user 0m29.435s</td></tr>
</tbody></table>
<br />
<div>
The result is that building Qt takes a tad longer, but building the client application is significantly faster. Which means development iterations will be much faster. In this instance, I used the textedit example in Qt. The build time went from 2 minutes and 46 seconds for a default non object files build, to 29 seconds! Zip! Zoom! Zang! Dang! that's fast (for wasm)</div>
<div>
<br /></div>
<div>
Unfortunately, this is not currently done by default by either Qt, emscripten or llvm. There is a way to do this which involves building llvm from upstream and telling emscripten to use that.</div>
<div>
<br /></div>
<div>
For the emscripten and binaryen version, I tested the current 1.38.32</div>
<div>
<br />
From this bug report <a href="https://bugreports.qt.io/browse/QTBUG-72537">https://bugreports.qt.io/browse/QTBUG-72537</a><br />
<div>
Morten has fleshed out a method for doing this.</div>
</div>
<div>
<div style="color: #172b4d; font-size: 14px;">
<br /></div>
<div style="color: #172b4d; font-size: 14px;">
------------------------------------------------------------</div>
<h3 style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 16px; font-weight: 500; letter-spacing: -0.006em; line-height: 1.25; margin: 30px 0px 0px; padding: 0px;">
Getting started:</h3>
<div style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 14px; margin-top: 10px; padding: 0px;">
(Note: this is "how-I-did-it", not necessarily "how-it-should-be-done".)</div>
<div style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 14px; margin-top: 10px; padding: 0px;">
1. Clone the following repositories:</div>
<ul style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 14px; margin: 10px 0px 0px;">
<li>emscripten : <a class="external-link" href="https://github.com/kripken/emscripten.git" rel="nofollow" style="color: #0052cc; cursor: pointer; text-decoration-line: none;">https://github.com/kripken/emscripten.git</a></li>
<li>binaryen: <a class="external-link" href="https://github.com/WebAssembly/binaryen.git" rel="nofollow" style="color: #0052cc; cursor: pointer; text-decoration-line: none;">https://github.com/WebAssembly/binaryen.git</a></li>
<li>llvm, <a class="external-link" href="https://github.com/llvm/llvm-project.git" rel="nofollow" style="color: #0052cc; cursor: pointer; text-decoration-line: none;">https://github.com/llvm/llvm-project.git</a></li>
</ul>
<div>
<span style="color: #172b4d; font-family: , "blinkmacsystemfont" , "segoe ui" , "roboto" , "oxygen" , "ubuntu" , "fira sans" , "droid sans" , "helvetica neue" , sans-serif;"><span style="font-size: 14px;"><br /></span></span></div>
<div style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 14px; margin-top: 10px; padding: 0px;">
2. Check out the version you want to use </div>
<ul style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 14px; margin: 10px 0px 0px;">
<li>emscripten and binaryen have matching emsdk version tags, for example "1.38.23".</li>
<li>llvm has its own version numbers, and must be synced up manually. Currently:</li>
<ul style="margin: 0px;">
<li>emscripten <= 1.38.23 : llvm 8.0 (8.0.0-rc2 branch)</li>
<li>emscripten > 1.38.23 : llvm 9.0 (master branch)<br />(em++/emcc will complain if you have the incorrect llvm version)</li>
</ul>
</ul>
<div>
<span style="color: #172b4d; font-family: , "blinkmacsystemfont" , "segoe ui" , "roboto" , "oxygen" , "ubuntu" , "fira sans" , "droid sans" , "helvetica neue" , sans-serif;"><span style="font-size: 14px;"><br /></span></span></div>
<div style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 14px; margin-top: 10px; padding: 0px;">
3. Build</div>
<ul style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 14px; margin: 10px 0px 0px;">
<li>emscripten is all python, no build needed.</li>
<li>binaryen: "cmake -GNinja && ninja"</li>
<li>llvm:<div class="code panel" style="background: rgb(244, 245, 247); border-radius: 3px; border: 1px solid rgb(193, 199, 208); font-family: monospace; font-size: 12px; line-height: 1.33333; margin: 9px 0px; padding: 0px;">
<div class="codeContent panelContent" style="margin: 0px; padding: 9px 12px;">
<pre class="code-java" style="max-height: 30em; overflow-wrap: normal; overflow: auto; padding: 0px; white-space: pre-wrap;">cmake -GNinja
../path/to/llvmcheckout
-DLLVM_ENABLE_PROJECTS=<span class="code-quote" style="color: #009100;">"clang;libcxx;libcxxabi;lld"</span>
-DCMAKE_BUILD_TYPE=Release
-DLLVM_TARGETS_TO_BUILD=WebAssembly
-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly
</pre>
</div>
</div>
<div style="margin-top: 10px; padding: 0px;">
(one of the WebAssembly targets may be redundant)</div>
</li>
</ul>
<div>
<span style="color: #172b4d; font-family: , "blinkmacsystemfont" , "segoe ui" , "roboto" , "oxygen" , "ubuntu" , "fira sans" , "droid sans" , "helvetica neue" , sans-serif;"><span style="font-size: 14px;"><br /></span></span></div>
<div style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 14px; margin-top: 10px; padding: 0px;">
4. Configure Environment</div>
<div style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 14px; margin-top: 10px; padding: 0px;">
I use the following:</div>
<div class="code panel" style="background: rgb(244, 245, 247); border-radius: 3px; border: 1px solid rgb(193, 199, 208); color: #172b4d; font-family: monospace; font-size: 14px; line-height: 1.33333; margin: 9px 0px; padding: 0px;">
<div class="codeContent panelContent" style="margin: 0px; padding: 9px 12px;">
<pre class="code-java" style="max-height: 30em; overflow-wrap: normal; overflow: auto; padding: 0px; white-space: pre-wrap;">export EMSDK=<span class="code-quote" style="color: #009100;">"/Users/msorvig/dev/emsdks/emscripten-1.38.23"</span>
export PATH=<span class="code-quote" style="color: #009100;">"$EMSDK:$PATH"</span>
export LLVM=<span class="code-quote" style="color: #009100;">"/Users/msorvig/dev/emsdks/llvm-8.0.0-build/bin"</span>
export BINARYEN=<span class="code-quote" style="color: #009100;">"/Users/msorvig/dev/emsdks/binaryen-1.38.23"</span>
export EM_CONFIG=<span class="code-quote" style="color: #009100;">"/Users/msorvig/dev/emsdks/.emscripten-vanillallvm-1.38.23"</span>
export EM_CACHE=<span class="code-quote" style="color: #009100;">"/Users/msorvig/dev/emsdks/.emscripten-vanillallvm-cache-1.38.23"</span>
</pre>
</div>
</div>
<div style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 14px; margin-top: 10px; padding: 0px;">
Where .emscripten-vanillallvm-1.38.23 is a copy of the .emscripten config file that emsdk generates.</div>
<div style="background-color: white; margin-top: 10px; padding: 0px;">
<div style="color: #172b4d; font-size: 14px;">
<br /></div>
<div style="color: #172b4d; font-size: 14px;">
------------------------------------------------------------</div>
<div style="color: #172b4d; font-size: 14px;">
<br /></div>
<span style="color: #172b4d;"><span style="font-size: 14px;">You will need to adjust the various paths to your system, of course. </span></span><br />
<span style="color: #172b4d;"><span style="font-size: 14px;">T</span></span><span style="color: #172b4d; font-size: 14px;">hen to configure and build Qt, add -device-option WASM_OBJECT_FILES=1 </span><span style="color: #172b4d; font-size: 14px;">to the normal Qt for WebAssembly configure line.</span><br />
<br />
<span style="color: #172b4d;"><span style="font-size: 14px;">The one I normally use is:</span></span><br />
<br />
<span style="color: #172b4d;"><span style="font-size: 14px;">configure -xplatform wasm-emscripten -developer-build -nomake tests -nomake </span></span><span style="color: #172b4d; font-size: 14px;">examples -opensource -confirm-license -verbose -compile-examples </span><span style="color: #172b4d; font-size: 14px;">-no-warnings-are-errors -release -device-option WASM_OBJECT_FILES=1</span><br />
<br />
<span style="color: #172b4d;"><span style="font-size: 14px;"><br /></span></span>
<span style="color: #172b4d;"><span style="font-size: 14px;">Works like a charm for the Qt 5.13 and the 5.13.0 branches of the Qt source code repo. I tried this with the 5.13.0 beta4 WebAssembly binaries, but got:</span></span><br />
<span style="color: #172b4d;"><span style="font-size: 14px;"><br /></span></span>
<span style="color: #172b4d;"><span style="font-size: 14px;">wasm-ld: warning: function signature mismatch:</span></span><br />
<span style="color: #172b4d;"><span style="font-size: 14px;">so a complete rebuild is required.</span></span><br />
<br /></div>
<div style="background-color: white; margin-top: 10px; padding: 0px;">
<span style="font-family: "times" , "times new roman" , serif;">There is a chapter regarding Qt for WebAssembly in the book, <a href="http://bit.ly/HandsOnMobileEmbedded" target="_blank">Hands-On Mobile and Embedded Development with Qt 5</a></span><br />
<br /></div>
<div style="background-color: white; color: #172b4d; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; font-size: 14px; margin-top: 10px; padding: 0px;">
<br /></div>
</div>
deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-81067852828451869452019-05-22T20:29:00.000+10:002019-05-22T20:29:35.143+10:00exec on Qt WebAssembly<span style="font-family: "times" , "times new roman" , serif;">When porting applications to Qt for WebAssembly, there are a few issues to keep in mind.</span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "times" , "times new roman" , serif;">One big issue is that you do not have a full operating system underneath for you to utilize.</span><br />
<span style="font-family: "times" , "times new roman" , serif;">What you get is the same sandbox in which javascript lives. In some aspects, it is somewhat similar to a single-threaded microcontroller where there is just one thread in which to operate, limited filesystem access and limited resources.</span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "times" , "times new roman" , serif;">One of the unusual and tricky bits about using Qt for WebAssembly, is that the exec loop is not typical. The Emscripten main loop is <span style="background-color: #fcfcfc; color: #404040; font-size: 16px;">co-operative, after each event has a turn to run, control is then returned to the browser. Any blocking of the event loop will mean the web page in which your application runs will become unresponsive.</span></span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "times" , "times new roman" , serif;">This is all fine and dandy for simple applications that use one main loop, but gets complicated when you try to exec a secondary loop. To stop the execution of an event loop, emscripten throws an exception, which leads to all kinds of fun. It also means it never returns to the same place that you expect it. So any modal dialog that uses exec() will not return values. Less than ideal.</span><br />
<br />
Take for instance QColorDialog. Typical use is as such:
<br />
<br />
<pre><!--EndFragment--></pre>
<pre></pre>
<pre> QColorDialog dlg(parent);
dlg.exec();
QColor<span style="color: silver;"> </span>color<span style="color: silver;"> </span><span style="color: #aaaaaa;">=</span><span style="color: silver;"> </span>dlg.selectedColor();</pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<pre></pre>
<span style="font-family: "times" , "times new roman" , serif;">Which is basically what QColorDialog::getColor does.</span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span><span style="font-family: "times" , "times new roman" , serif;">... and does not work with Qt WebAssembly, because exec() does not return to the same place as you expect! The call to selectedColor will never get called.</span><br />
<span style="font-family: "times" , "times new roman" , serif;"><br /></span>
<span style="font-family: "times" , "times new roman" , serif;">What you can do is use a non-modal dialog with the show() or in the case of QColorDialog open() function and use Qt's signal/slot API to retrieve the selected QColor.</span><br />
<br />
<pre> QColorDialog *dlg = new QColorDialog(this);
connect(
dlg, &QColorDialog::colorSelected,
[=](const QColor &selectedColor) {
qDebug() << Q_FUNC_INFO << selectedColor;
});
dlg->open();</pre>
<div>
<br /></div>
You can read more about the Emscripten execution environment at<br />
<a href="https://emscripten.org/docs/api_reference/emscripten.h.html#browser-execution-environment">https://emscripten.org/docs/api_reference/emscripten.h.html#browser-execution-environment</a><br />
<br />
You can also learn more about Qt for WebAssembly and other things in the book I wrote:<br />
<a href="http://bit.ly/HandsOnMobileDevQt5" target="_blank">Hands on Mobile and Embedded Development with Qt 5</a><br />
<br />deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-60039374006640453482019-05-03T10:58:00.000+10:002019-05-03T10:58:04.432+10:00Mobile and Embedded Development with QtAt times it was a bit painful juggling writing a book, doing my day job and running around doing the things that life throws. It's done and dusted now, you too can buy my book titled <b>Hands-On Mobile and Embedded Development with Qt 5</b>! It has a nice image of glacier ice on the cover, which I thought was appropriate for a technology founded in Norway and then continued in Finland.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-XDVutGIIjUQ/XMtF5a79uFI/AAAAAAAAaas/z8B0BiOzy24We6UDss_f6SOfX8CiMp9jwCLcBGAs/s1600/book-cover.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1298" height="320" src="https://1.bp.blogspot.com/-XDVutGIIjUQ/XMtF5a79uFI/AAAAAAAAaas/z8B0BiOzy24We6UDss_f6SOfX8CiMp9jwCLcBGAs/s320/book-cover.jpg" width="259" /></a></div>
<br />
<br />
<a href="https://www.packtpub.com//application-development/hands-mobile-and-embedded-development-qt-5" target="_blank">https://www.packtpub.com/application-development/hands-mobile-and-embedded-development-qt-5</a><br />
<br />
A big thanks to the co-founder of Trolltech, Eirik Chambe-Eng, who was gracious enough to write the forward at the last second. Tons of thanks to all the editors who also worked on this book.<br />
<br />
One of the things I learned writing this book is that Qt is big. I already knew that, but now it's plainly apparent just how big it has grown. Not only are there major companies developing products with Qt, but it has a lot of different functionality and is not just about desktop widgets. There are a huge number of classes to cover.<br />
<br />
You can check out the table of contents if you want to see what is covered and included. One area that I did not include is OpenGL ES. This is a huge topic and easily a book on it's own. It's something I would like to know more about, which is why I did not feel qualified to cover it. You need to know OpenGL Shader Language (GLSL), and I did not have the time to discover that to any real depth.<br />
<br />
I hope that I covered topics that are relevant for mobile, embedded and IoT developers. From QtWidgets, QtQuick, QtSensors (of course) to In-app purchasing and building an embedded system with Qt Company's Boot To Qt and Yocto. I also explore Qt's newest platform - Qt for WebAssembly, which allows you to serve Qt applications from a web server to run in a web browser.<br />
<br />
<br />
Enjoy!deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0tag:blogger.com,1999:blog-2997739907109253354.post-78819947181393305012017-11-22T11:52:00.000+10:002017-11-22T11:52:08.754+10:00Qt for WebAssembly updateThe project Qt 5 emscripten that <a href="https://www.intopalo.com/" target="_blank">Intopalo</a> (who are doing tons of amazing stuff with some awesome talent) started as research for a client, has moved git repos. The new main git repo has been moved to a branch in Qt proper! and a new name - Qt for WebAssembly.<br />
<br />
git://code.qt.io/qt/qtbase.git in the 'wip/webassembly' branch, and can be downloaded here:<br />
git clone -b wip/webassembly git://code.qt.io/qt/qtbase.git<br />
<br />
which is also here at github:<br />
https://github.com/qt/qtbase/tree/wip/webassembly<br />
<br />
Along with the move comes expanded requirements, like targeting non QtWidgets based apps, including QtDeclarative.<br />
<br />
As well, it now builds only for wasm and does not bother with the asmjs fallback, This means your web browser will need to specifically support wasm. Pure wasm builds in emscripten have performance optimizations that do not happen when also building the asmjs fallback.<br />
<br />
That said....<br />
<br />
<b>*NOTE* </b>Qt for WebAssembly is still very alpha and buggy! Some of which I will cover in the next blog post.<br />
<br />
I have managed to conjole Qt Creator to use the emscripten built Qt for apps. Here are the steps to do so, if you are so inclined.<br />
<br />
Build Qt for web assembly.<br />
<br />
1) <b>Download and build emsdk</b> (you may also try to pre built binary version)<br />
<br />
git the emsdk source repo:<br />
git clone https://github.com/juj/emsdk.git<br />
(to update: git pull; ./emsdk update-tags )<br />
<br />
Follow these instructions to build emsdk:<br />
http://kripken.github.io/emscripten-site/docs/getting_started/downloads.html#installation-instructions<br />
<br />
I usually use 'incoming' instead of 'latest', as it has more recent features.<br />
<br />
/path/to/emsdk install sdk-master-64bit binaryen-master-64bit<br />
<br />
2) <b>Download Qt for emsdk</b><br />
<br />
git clone -b wip/webassembly <a class="external-link" href="https://code.qt.io/qt/qtbase.git" rel="nofollow" style="background-color: white; color: #3b73af; cursor: pointer; font-family: Arial, sans-serif; font-size: 16px; text-decoration-line: none;" title="Follow link">https://code.qt.io/qt/qtbase.git</a><br />
<br />
3) <b>Build Qt</b><br />
<br />
source /path/to/emsdk/emsdk_env.sh<br />
<br />
$QT_DIR/configure -xplatform emscripten -confirm-license -opensource -nomake tests -nomake examples -developer-build -no-dbus -no-headersclean -release -no-thread -no-feature-networkinterface<br />
<br />
(non developer builds should work too)<br />
<br />
4) <b>Add emscripten compiler to Creator</b><br />
<br />
<ul>
<li>Tools->Options->Build & Run -> Compilers->Add->Custom</li>
<li>add emcc as C, and emc++ as C++</li>
<li>'emscripten' as Qt mkspecs.</li>
<li>ABI: x86, linux (or whatever platform you build on), unknown, elf, 64bit</li>
</ul>
<br />
5) <b>Add emscripten built Qt</b><br />
<br />
<ul>
<li>Tools->Options->Build & Run ->Qt Versions->Add</li>
<ul>
<li>add the qmake from your emscripten Qt build.</li>
</ul>
</ul>
6) <b>Add emscripten kit</b><br />
<br />
<ul>
<li>Tools->Options->Build & Run ->Kits->Add</li>
<ul>
<li>add emcc and emc++ as Compilers</li>
<li>If you use cmake, you can also add to CMake configuration:</li>
<ul>
<li>CMAKE_TOOLCHAIN_FILE=/path/to/emsdk/emscripten/incoming/cmake/Modules/Platform/Emscripten.cmake
</li>
</ul>
</ul>
</ul>
7) <b>Build your app</b><br />
<div>
<ul>
<li>Use '<b>release</b>' mode build, as 'debug' will result in a runtime error: "failed to asynchronously prepare wasm: CompileError: wasm validation error: at offset 14762: too many functions"</li>
</ul>
8) <b>Run your app</b><br />
<br />
<ul>
<li>add custom executable: Projects->(qt5-wasm) Run</li>
<ul>
<li>specify /path/to/emsdk/emscripten/incoming/emrun</li>
<ul>
<li>arguments: <target>.html</target></li>
<ul>
<li>I also use --browser firefox (I use nightly)</li>
</ul>
</ul>
</ul>
</ul>
Hopefully some things will actually work! This is all still in the development phase. Your kilometerage will vary. </div>
<div>
<br /></div>
<div>
Good luck! You can join the fun, we are in #qt-webassembly at <a href="https://qtmob.slack.com/" target="_blank">https://qtmob.slack.com</a> and on freenode.<br />
<br />
<br /></div>
deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com1tag:blogger.com,1999:blog-2997739907109253354.post-28872440386448553022017-06-05T14:33:00.002+10:002017-06-05T14:33:57.680+10:00Qt for web (assembly)So ya, I have been doing work for the up and coming Finnish company <a href="https://www.intopalo.com/" target="_blank">Intopalo</a> getting <a href="https://codereview.qt-project.org/#/c/178543/" target="_blank">Qt5 for WebAssembly</a> using emscripten up and running. This is the 3rd Finnish company I have done work for, and the 4th Nordic company (Trolltech, Nokia, Jolla, Intopalo)<br />
<br />
This is very much a work in progress.<br />
<br />
<b>Background:</b><br />
<br />
There was a Qt4 version, <a href="http://vps2.etotheipiplusone.com:30176/redmine/projects/emscripten-qt/wiki" target="_blank">emscripten-qt</a> and a related <a href="https://blog.qt.io/blog/2015/09/25/qt-for-native-client-and-emscripten/" target="_blank">Qt5 for Native Client</a> which do similar things, and we have taken inspiration from both these projects. Thanks!<br />
<br />
<b>The gist of it:</b><br />
<br />
We use <a href="https://kripken.github.io/emscripten-site/docs/getting_started/downloads.html" target="_blank">emscripten</a> to cross compile Qt5 into javascript and/or webassembly. We use 'incoming' from emscripten's <a href="https://github.com/juj/emsdk" target="_blank">git repo</a>. Details how to build emsdk are <a href="https://kripken.github.io/emscripten-site/docs/building_from_source/building_emscripten_from_source_using_the_sdk.html" target="_blank">here</a>.<br />
<br />
My working Qt5 repo is <a href="https://github.com/lpotter/qt5-emscripten/tree/wm" target="_blank">here</a>, (the wm branch contains multi window and window decorations) while there is also an outdated<a href="https://codereview.qt-project.org/#/c/178543/" target="_blank"> WIP gerrit MR </a>which will get updated at some point.<br />
<br />
This is how I configure Qt for emscripten:<br />
. ~/emsdk/emsdk_env.sh (to get em compiler in path)<br />
<br />
~/depot/<span>qt</span>/qt5/qtbase/configure -xplatform emscripten
-confirm-license -opensource -nomake tests -nomake examples
-developer-build -no-dbus -no-thread<br />
<br />
(no-thread is a new feature I added for this, as threading in javascript is basically non existent)<br />
<br />
and then, use that qmake to compile a Qt app!<br />
<br />
To run the app, in the firefox browser<br />
emrun --browser firefox path/to/app.html <br />
<br />
This is all bleeding edge from the emscripten compiler to the web assembly support in the browser, so your mileage may vary.<br />
<br />
With the 'wm' branch of my github repo, multi windows and window decorations are kind of working. As well as compiling into webassembly and not just javascript asmjs.<br />
Compiling into wasm brings much smaller download sizes, but you need to have a browser that supports it.<br />
<br />
There is still heaps of work to do to bring it up to release state. So for now, it is still a research and WIP project, and a lot of things may or may not work. But it can run some Qt widget based apps in your (firefox) browser!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://1.bp.blogspot.com/-jUiQmevDj_Q/WTTe9FWhwaI/AAAAAAAAK60/Y6ip1wGoTd4vShLXGbIiUg3rURrMXikYwCLcB/s1600/qt-wasm.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1060" data-original-width="1504" height="225" src="https://1.bp.blogspot.com/-jUiQmevDj_Q/WTTe9FWhwaI/AAAAAAAAK60/Y6ip1wGoTd4vShLXGbIiUg3rURrMXikYwCLcB/s320/qt-wasm.png" width="320" /></a></div>
<br />
<b>The future: </b><br />
<br />
Currently there is no support for qml/qt quick. That just means we haven't tried it as our focus is currently on widgets and opengl.<br />
<br />
Personally, I would like to see location and some sensor events, but that is for the future.deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com5tag:blogger.com,1999:blog-2997739907109253354.post-36527636993393138452017-05-05T07:39:00.000+10:002017-05-05T09:18:25.780+10:00new QtSensor IoT plugins!I've said it before and I will say it again... one thing I am tired of hearing about, is that people think Qt is only for UI applications.<br />
WRONG!<br />
Qt is great for IoT as well! even without a UI. (IoT is today's buzzword for 'embedded', which really isn't embedded, just small) and you cannot have IoT without sensors!<br />
<br />
I just submitted a MR to QtSensors to add support for the <a href="https://www.raspberrypi.org/products/sense-hat/" target="_blank">SenseHAT</a> sensor board for raspberry pi:<br />
<a href="https://www.blogger.com/goog_1080328120"><br /></a>
<a href="https://codereview.qt-project.org/#/c/193456">https://codereview.qt-project.org/#/c/193456</a><br />
For raspberry pi, you will need <a href="https://github.com/RTIMULib/RTIMULib2" target="_blank">RTIMULib</a> installed on your rpi (and dev package to compile QtSensors). It's in the default repo, so you don't actually need to compile RTIMULib yourself.<br />
<br />
Even though this is submitted for dev branch and should be available in Qt Sensors 5.10, this will compile and work for the Qt 5.3 that comes with the raspbian distribution.<br />
<br />
As well, if my spare time allows, I will also get my <a href="https://creator.matrix.one/#!/index" target="_blank">Matrix Creator</a> QtSensor plugin into shape to submit as well.<br />
<br />
I also have planned getting the <a href="http://www.ti.com/ww/en/wireless_connectivity/sensortag/" target="_blank">TI SensorTag</a> code I wrote shoved into a QtSensor plugin. What's special about the SensorTag is that it has (buzzword alert) wireless sensors! This is a bit more tricky as it will use and be dependent on Qt's <a href="https://doc.qt.io/qt-5/qlowenergyservice.html" target="_blank">QLowEnergyService</a> but will work nonetheless. This plugin doesn't run on the device, but requires a host machine which has bluetooth.<br />
<br />deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com2tag:blogger.com,1999:blog-2997739907109253354.post-47767151953277709912017-01-07T12:09:00.000+10:002017-01-07T12:48:15.380+10:00Movin' on...A year has gone by since I started work with Canonical. As it turns out, I must be on my way. Where to? Not real sure at this moment, there seems plenty of companies using Qt & QML these days. \0/<br />
<div>
<br /></div>
<div>
But saying that, I am open to suggestions. <a href="https://www.linkedin.com/in/lornpotter" target="_blank">LinkedIn</a><br />
</div>
<div>
Plenty of IoT and devices using sensors around. Heck, even Moto Z phone has some great uses for sensor gestures similar to what I wrote for QtSensors while I was at Nokia.<br />
<div>
<div>
<br /></div>
<div>
But a lack of companies that allow freelance or remote work. The last few years I have worked remotely doing work for Jolla and Canonical. Both fantastic companies to work for, which really have it together for working remotely.</div>
<div>
<br /></div>
<div>
I am still surprised that only a handful of companies regularly allow remote work. I do not miss the stuffy non window opening offices and the long daily commute, which sometimes means riding a motorcycle through hail! (I do not suggest this for anyone)</div>
<div>
<br /></div>
<div>
Of course, I am still maintainer for QtSensors, QtSystemInfo for the Qt Project, and Sensor Framework for Mer, and always dreaming up new ways to use sensors. Still keeping tabs on QtNetwork bearer classes.</div>
</div>
<div>
<br /></div>
<div>
Although I had to send back the Canonical devices, I still have Ubuntu on my Nexus 4. I still have my Jolla phones and tablet.</div>
<div>
<br /></div>
<div>
That said, I still have this blog here, and besides spending my time looking for a new programming gig, I am (always) preparing to release a new album. <a href="http://llornkcor.com/">http://llornkcor.com</a></div>
<div>
and always willing to work with anyone needing music/audio/soundtrack work.</div>
<div>
<br /></div>
</div>
deviceguyhttp://www.blogger.com/profile/08211459741197454860noreply@blogger.com0