diff options
43 files changed, 1510 insertions, 50 deletions
diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro index cc75cbc0d3..08ca18fa0c 100644 --- a/examples/quick/scenegraph/scenegraph.pro +++ b/examples/quick/scenegraph/scenegraph.pro @@ -27,7 +27,9 @@ win32 { } qtConfig(vulkan) { - SUBDIRS += vulkanunderqml + SUBDIRS += \ + vulkanunderqml \ + vulkantextureimport } EXAMPLE_FILES += \ diff --git a/examples/quick/scenegraph/vulkantextureimport/doc/images/vulkantextureimport-example.jpg b/examples/quick/scenegraph/vulkantextureimport/doc/images/vulkantextureimport-example.jpg Binary files differnew file mode 100644 index 0000000000..e7bbe62bf3 --- /dev/null +++ b/examples/quick/scenegraph/vulkantextureimport/doc/images/vulkantextureimport-example.jpg diff --git a/examples/quick/scenegraph/vulkantextureimport/doc/src/vulkantextureimport.qdoc b/examples/quick/scenegraph/vulkantextureimport/doc/src/vulkantextureimport.qdoc new file mode 100644 index 0000000000..334291f990 --- /dev/null +++ b/examples/quick/scenegraph/vulkantextureimport/doc/src/vulkantextureimport.qdoc @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example scenegraph/vulkantextureimport + \title Scene Graph - Vulkan Texture Import + \ingroup qtquickexamples + \brief Shows how to use a texture created directly with use a texture created directly withulkan. + + \image vulkantextureimport-example.jpg + + + The Vulkan Texture Import example shows how an application can import and + use a + \l{https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VkImage.html} + {VkImage} in the Qt Quick scene. This provides an alternative to the \l{Scene Graph - + Vulkan Under QML}{underlay}, overlay, or \l{Scene Graph - Custom Rendering + with QSGRenderNode}{render node} approaches when it comes to integrating + native Vulkan rendering. + + This example is equivalent in most ways to the \l{Scene Graph - Metal Texture Import}{Metal Texture Import} + example. The Vulkan rendering code is taken from the \l{Scene Graph - Vulkan Under QML}{Vulkan Under QML} example. + + */ diff --git a/examples/quick/scenegraph/vulkantextureimport/main.cpp b/examples/quick/scenegraph/vulkantextureimport/main.cpp new file mode 100644 index 0000000000..1c42e87043 --- /dev/null +++ b/examples/quick/scenegraph/vulkantextureimport/main.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QGuiApplication> +#include <QtQuick/QQuickView> +#include "vulkantextureimport.h" + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + qmlRegisterType<CustomTextureItem>("VulkanTextureImport", 1, 0, "CustomTextureItem"); + + QQuickWindow::setSceneGraphBackend(QSGRendererInterface::VulkanRhi); + + QQuickView view; + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///scenegraph/vulkantextureimport/main.qml")); + view.resize(400, 400); + view.show(); + + return app.exec(); +} diff --git a/examples/quick/scenegraph/vulkantextureimport/main.qml b/examples/quick/scenegraph/vulkantextureimport/main.qml new file mode 100644 index 0000000000..91adf4c394 --- /dev/null +++ b/examples/quick/scenegraph/vulkantextureimport/main.qml @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +//! [1] +import VulkanTextureImport 1.0 +//! [1] + +Rectangle { + gradient: Gradient { + GradientStop { position: 0; color: "firebrick" } + GradientStop { position: 1; color: "black" } + } + + //! [2] + CustomTextureItem { + id: renderer + anchors.fill: parent + anchors.margins: 10 + + SequentialAnimation on t { + NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad } + NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad } + loops: Animation.Infinite + running: true + } + //! [2] + + transform: [ + Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; }, + Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 }, + Scale { id: scale; }, + Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 } + ] + } + + SequentialAnimation { + PauseAnimation { duration: 5000 } + ParallelAnimation { + NumberAnimation { target: scale; property: "xScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack } + NumberAnimation { target: scale; property: "yScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack } + } + NumberAnimation { target: rotation; property: "angle"; to: 80; duration: 1000; easing.type: Easing.InOutCubic } + NumberAnimation { target: rotation; property: "angle"; to: -80; duration: 1000; easing.type: Easing.InOutCubic } + NumberAnimation { target: rotation; property: "angle"; to: 0; duration: 1000; easing.type: Easing.InOutCubic } + NumberAnimation { target: renderer; property: "opacity"; to: 0.1; duration: 1000; easing.type: Easing.InOutCubic } + PauseAnimation { duration: 1000 } + NumberAnimation { target: renderer; property: "opacity"; to: 1.0; duration: 1000; easing.type: Easing.InOutCubic } + ParallelAnimation { + NumberAnimation { target: scale; property: "xScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack } + NumberAnimation { target: scale; property: "yScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack } + } + running: true + loops: Animation.Infinite + } + + Rectangle { + id: labelFrame + anchors.margins: -10 + radius: 5 + color: "white" + border.color: "black" + opacity: 0.5 + anchors.fill: label + } + + Text { + id: label + anchors.bottom: renderer.bottom + anchors.left: renderer.left + anchors.right: renderer.right + anchors.margins: 20 + wrapMode: Text.WordWrap + text: "The squircle, using rendering code borrowed from the vulkanunderqml example, is rendered into a texture directly with Vulkan. The VkImage is then imported and used in a custom Qt Quick item." + } +} diff --git a/examples/quick/scenegraph/vulkantextureimport/squircle.frag.spv b/examples/quick/scenegraph/vulkantextureimport/squircle.frag.spv Binary files differnew file mode 100644 index 0000000000..e4d13a871d --- /dev/null +++ b/examples/quick/scenegraph/vulkantextureimport/squircle.frag.spv diff --git a/examples/quick/scenegraph/vulkantextureimport/squircle.vert.spv b/examples/quick/scenegraph/vulkantextureimport/squircle.vert.spv Binary files differnew file mode 100644 index 0000000000..5df94a47e4 --- /dev/null +++ b/examples/quick/scenegraph/vulkantextureimport/squircle.vert.spv diff --git a/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.cpp b/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.cpp new file mode 100644 index 0000000000..a4201c1dfa --- /dev/null +++ b/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.cpp @@ -0,0 +1,823 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "vulkantextureimport.h" + +#include <QtGui/QScreen> +#include <QtQuick/QQuickWindow> +#include <QtQuick/QSGTextureProvider> +#include <QtQuick/QSGSimpleTextureNode> + +#include <QVulkanInstance> +#include <QVulkanFunctions> + +class CustomTextureNode : public QSGTextureProvider, public QSGSimpleTextureNode +{ + Q_OBJECT + +public: + CustomTextureNode(QQuickItem *item); + ~CustomTextureNode() override; + + QSGTexture *texture() const override; + + void sync(); + +private slots: + void render(); + +private: + enum Stage { + VertexStage, + FragmentStage + }; + void prepareShader(Stage stage); + bool buildTexture(const QSize &size); + void freeTexture(); + bool createRenderPass(); + bool initialize(); + + QQuickItem *m_item; + QQuickWindow *m_window; + QSize m_size; + qreal m_dpr; + + QByteArray m_vert; + QByteArray m_frag; + + VkImage m_texture = VK_NULL_HANDLE; + VkDeviceMemory m_textureMemory = VK_NULL_HANDLE; + VkFramebuffer m_textureFramebuffer = VK_NULL_HANDLE; + VkImageView m_textureView = VK_NULL_HANDLE; + + bool m_initialized = false; + + float m_t; + + VkPhysicalDevice m_physDev = VK_NULL_HANDLE; + VkDevice m_dev = VK_NULL_HANDLE; + QVulkanDeviceFunctions *m_devFuncs = nullptr; + QVulkanFunctions *m_funcs = nullptr; + + VkBuffer m_vbuf = VK_NULL_HANDLE; + VkDeviceMemory m_vbufMem = VK_NULL_HANDLE; + VkBuffer m_ubuf = VK_NULL_HANDLE; + VkDeviceMemory m_ubufMem = VK_NULL_HANDLE; + VkDeviceSize m_allocPerUbuf = 0; + + VkPipelineCache m_pipelineCache = VK_NULL_HANDLE; + + VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE; + VkDescriptorSetLayout m_resLayout = VK_NULL_HANDLE; + VkPipeline m_pipeline = VK_NULL_HANDLE; + + VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE; + VkDescriptorSet m_ubufDescriptor = VK_NULL_HANDLE; + + VkRenderPass m_renderPass = VK_NULL_HANDLE; +}; + +CustomTextureItem::CustomTextureItem() +{ + setFlag(ItemHasContents, true); +} + +void CustomTextureItem::invalidateSceneGraph() // called on the render thread when the scenegraph is invalidated +{ + m_node = nullptr; +} + +void CustomTextureItem::releaseResources() // called on the gui thread if the item is removed from scene +{ + m_node = nullptr; +} + +QSGNode *CustomTextureItem::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) +{ + CustomTextureNode *n = static_cast<CustomTextureNode *>(node); + + if (!n && (width() <= 0 || height() <= 0)) + return nullptr; + + if (!n) { + m_node = new CustomTextureNode(this); + n = m_node; + } + + m_node->sync(); + + n->setTextureCoordinatesTransform(QSGSimpleTextureNode::NoTransform); + n->setFiltering(QSGTexture::Linear); + n->setRect(0, 0, width(), height()); + + window()->update(); // ensure getting to beforeRendering() at some point + + return n; +} + +void CustomTextureItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + QQuickItem::geometryChanged(newGeometry, oldGeometry); + + if (newGeometry.size() != oldGeometry.size()) + update(); +} + +void CustomTextureItem::setT(qreal t) +{ + if (t == m_t) + return; + + m_t = t; + emit tChanged(); + + update(); +} + +CustomTextureNode::CustomTextureNode(QQuickItem *item) + : m_item(item) +{ + m_window = m_item->window(); + connect(m_window, &QQuickWindow::beforeRendering, this, &CustomTextureNode::render); + connect(m_window, &QQuickWindow::screenChanged, this, [this]() { + if (m_window->effectiveDevicePixelRatio() != m_dpr) + m_item->update(); + }); +} + +CustomTextureNode::~CustomTextureNode() +{ + m_devFuncs->vkDestroyBuffer(m_dev, m_vbuf, nullptr); + m_devFuncs->vkDestroyBuffer(m_dev, m_ubuf, nullptr); + m_devFuncs->vkFreeMemory(m_dev, m_vbufMem, nullptr); + m_devFuncs->vkFreeMemory(m_dev, m_ubufMem, nullptr); + + m_devFuncs->vkDestroyPipelineCache(m_dev, m_pipelineCache, nullptr); + m_devFuncs->vkDestroyPipelineLayout(m_dev, m_pipelineLayout, nullptr); + m_devFuncs->vkDestroyPipeline(m_dev, m_pipeline, nullptr); + + m_devFuncs->vkDestroyRenderPass(m_dev, m_renderPass, nullptr); + + m_devFuncs->vkDestroyDescriptorSetLayout(m_dev, m_resLayout, nullptr); + m_devFuncs->vkDestroyDescriptorPool(m_dev, m_descriptorPool, nullptr); + + delete texture(); + freeTexture(); +} + +QSGTexture *CustomTextureNode::texture() const +{ + return QSGSimpleTextureNode::texture(); +} + +static const float vertices[] = { + -1, -1, + 1, -1, + -1, 1, + 1, 1 +}; + +const int UBUF_SIZE = 4; + + +bool CustomTextureNode::buildTexture(const QSize &size) +{ + VkImageCreateInfo imageInfo; + memset(&imageInfo, 0, sizeof(imageInfo)); + imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + imageInfo.flags = 0; + imageInfo.imageType = VK_IMAGE_TYPE_2D; + imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + imageInfo.extent.width = uint32_t(size.width()); + imageInfo.extent.height = uint32_t(size.height()); + imageInfo.extent.depth = 1; + imageInfo.mipLevels = 1; + imageInfo.arrayLayers = 1; + imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED; + + imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + VkImage image = VK_NULL_HANDLE; + if (m_devFuncs->vkCreateImage(m_dev, &imageInfo, nullptr, &image) != VK_SUCCESS) { + qCritical("VulkanWrapper: failed to create image!"); + return false; + } + + m_texture = image; + + VkMemoryRequirements memReq; + m_devFuncs->vkGetImageMemoryRequirements(m_dev, image, &memReq); + + quint32 memIndex = 0; + VkPhysicalDeviceMemoryProperties physDevMemProps; + m_window->vulkanInstance()->functions()->vkGetPhysicalDeviceMemoryProperties(m_physDev, &physDevMemProps); + for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) { + if (!(memReq.memoryTypeBits & (1 << i))) + continue; + memIndex = i; + } + + VkMemoryAllocateInfo allocInfo = { + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + nullptr, + memReq.size, + memIndex + }; + + VkResult err = m_devFuncs->vkAllocateMemory(m_dev, &allocInfo, nullptr, &m_textureMemory); + if (err != VK_SUCCESS) { + qWarning("Failed to allocate memory for linear image: %d", err); + return false; + } + + err = m_devFuncs->vkBindImageMemory(m_dev, image, m_textureMemory, 0); + if (err != VK_SUCCESS) { + qWarning("Failed to bind linear image memory: %d", err); + return false; + } + + VkImageViewCreateInfo viewInfo; + memset(&viewInfo, 0, sizeof(viewInfo)); + viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + viewInfo.image = image; + viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewInfo.format = imageInfo.format; + viewInfo.components.r = VK_COMPONENT_SWIZZLE_R; + viewInfo.components.g = VK_COMPONENT_SWIZZLE_G; + viewInfo.components.b = VK_COMPONENT_SWIZZLE_B; + viewInfo.components.a = VK_COMPONENT_SWIZZLE_A; + viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewInfo.subresourceRange.baseMipLevel = 0; + viewInfo.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS; + viewInfo.subresourceRange.baseArrayLayer = 0; + viewInfo.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS; + + err = m_devFuncs->vkCreateImageView(m_dev, &viewInfo, nullptr, &m_textureView); + if (err != VK_SUCCESS) { + qWarning("Failed to create render target image view: %d", err); + return false; + } + + VkFramebufferCreateInfo fbInfo; + memset(&fbInfo, 0, sizeof(fbInfo)); + fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + fbInfo.renderPass = m_renderPass; + fbInfo.attachmentCount = 1; + fbInfo.pAttachments = &m_textureView; + fbInfo.width = uint32_t(size.width()); + fbInfo.height = uint32_t(size.height()); + fbInfo.layers = 1; + + err = m_devFuncs->vkCreateFramebuffer(m_dev, &fbInfo, nullptr, &m_textureFramebuffer); + if (err != VK_SUCCESS) { + qWarning("Failed to create framebuffer: %d", err); + return false; + } + return true; +} + +void CustomTextureNode::freeTexture() +{ + if (m_texture) { + m_devFuncs->vkDestroyFramebuffer(m_dev, m_textureFramebuffer, nullptr); + m_textureFramebuffer = VK_NULL_HANDLE; + m_devFuncs->vkFreeMemory(m_dev, m_textureMemory, nullptr); + m_textureMemory = VK_NULL_HANDLE; + m_devFuncs->vkDestroyImageView(m_dev, m_textureView, nullptr); + m_textureView = VK_NULL_HANDLE; + m_devFuncs->vkDestroyImage(m_dev, m_texture, nullptr); + m_texture = VK_NULL_HANDLE; + } +} + + + +static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign) +{ + return (v + byteAlign - 1) & ~(byteAlign - 1); +} + +bool CustomTextureNode::createRenderPass() +{ + const VkFormat vkformat = VK_FORMAT_R8G8B8A8_UNORM; + const VkSampleCountFlagBits samples = VK_SAMPLE_COUNT_1_BIT; + VkAttachmentDescription colorAttDesc; + memset(&colorAttDesc, 0, sizeof(colorAttDesc)); + colorAttDesc.format = vkformat; + colorAttDesc.samples = samples; + colorAttDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colorAttDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colorAttDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + colorAttDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + colorAttDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + colorAttDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + const VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + + VkSubpassDescription subpassDesc; + memset(&subpassDesc, 0, sizeof(subpassDesc)); + subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDesc.colorAttachmentCount = 1; + subpassDesc.pColorAttachments = &colorRef; + subpassDesc.pDepthStencilAttachment = nullptr; + subpassDesc.pResolveAttachments = nullptr; + + VkRenderPassCreateInfo rpInfo; + memset(&rpInfo, 0, sizeof(rpInfo)); + rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + rpInfo.attachmentCount = 1; + rpInfo.pAttachments = &colorAttDesc; + rpInfo.subpassCount = 1; + rpInfo.pSubpasses = &subpassDesc; + + VkResult err = m_devFuncs->vkCreateRenderPass(m_dev, &rpInfo, nullptr, &m_renderPass); + if (err != VK_SUCCESS) { + qWarning("Failed to create renderpass: %d", err); + return false; + } + + return true; +} + +bool CustomTextureNode::initialize() +{ + const int framesInFlight = m_window->graphicsStateInfo().framesInFlight; + m_initialized = true; + + QSGRendererInterface *rif = m_window->rendererInterface(); + QVulkanInstance *inst = reinterpret_cast<QVulkanInstance *>( + rif->getResource(m_window, QSGRendererInterface::VulkanInstanceResource)); + Q_ASSERT(inst && inst->isValid()); + + m_physDev = *static_cast<VkPhysicalDevice *>(rif->getResource(m_window, QSGRendererInterface::PhysicalDeviceResource)); + m_dev = *static_cast<VkDevice *>(rif->getResource(m_window, QSGRendererInterface::DeviceResource)); + Q_ASSERT(m_physDev && m_dev); + + m_devFuncs = inst->deviceFunctions(m_dev); + m_funcs = inst->functions(); + Q_ASSERT(m_devFuncs && m_funcs); + + createRenderPass(); + + VkPhysicalDeviceProperties physDevProps; + m_funcs->vkGetPhysicalDeviceProperties(m_physDev, &physDevProps); + + VkPhysicalDeviceMemoryProperties physDevMemProps; + m_funcs->vkGetPhysicalDeviceMemoryProperties(m_physDev, &physDevMemProps); + + VkBufferCreateInfo bufferInfo; + memset(&bufferInfo, 0, sizeof(bufferInfo)); + bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + bufferInfo.size = sizeof(vertices); + bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + VkResult err = m_devFuncs->vkCreateBuffer(m_dev, &bufferInfo, nullptr, &m_vbuf); + if (err != VK_SUCCESS) + qFatal("Failed to create vertex buffer: %d", err); + + VkMemoryRequirements memReq; + m_devFuncs->vkGetBufferMemoryRequirements(m_dev, m_vbuf, &memReq); + VkMemoryAllocateInfo allocInfo; + memset(&allocInfo, 0, sizeof(allocInfo)); + allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocInfo.allocationSize = memReq.size; + + uint32_t memTypeIndex = uint32_t(-1); + const VkMemoryType *memType = physDevMemProps.memoryTypes; + for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) { + if (memReq.memoryTypeBits & (1 << i)) { + if ((memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) + && (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) + { + memTypeIndex = i; + break; + } + } + } + if (memTypeIndex == uint32_t(-1)) + qFatal("Failed to find host visible and coherent memory type"); + + allocInfo.memoryTypeIndex = memTypeIndex; + err = m_devFuncs->vkAllocateMemory(m_dev, &allocInfo, nullptr, &m_vbufMem); + if (err != VK_SUCCESS) + qFatal("Failed to allocate vertex buffer memory of size %u: %d", uint(allocInfo.allocationSize), err); + + void *p = nullptr; + err = m_devFuncs->vkMapMemory(m_dev, m_vbufMem, 0, allocInfo.allocationSize, 0, &p); + if (err != VK_SUCCESS || !p) + qFatal("Failed to map vertex buffer memory: %d", err); + memcpy(p, vertices, sizeof(vertices)); + m_devFuncs->vkUnmapMemory(m_dev, m_vbufMem); + err = m_devFuncs->vkBindBufferMemory(m_dev, m_vbuf, m_vbufMem, 0); + if (err != VK_SUCCESS) + qFatal("Failed to bind vertex buffer memory: %d", err); + + m_allocPerUbuf = aligned(UBUF_SIZE, physDevProps.limits.minUniformBufferOffsetAlignment); + + bufferInfo.size = framesInFlight * m_allocPerUbuf; + bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + err = m_devFuncs->vkCreateBuffer(m_dev, &bufferInfo, nullptr, &m_ubuf); + if (err != VK_SUCCESS) + qFatal("Failed to create uniform buffer: %d", err); + m_devFuncs->vkGetBufferMemoryRequirements(m_dev, m_ubuf, &memReq); + memTypeIndex = -1; + for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) { + if (memReq.memoryTypeBits & (1 << i)) { + if ((memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) + && (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) + { + memTypeIndex = i; + break; + } + } + } + if (memTypeIndex == uint32_t(-1)) + qFatal("Failed to find host visible and coherent memory type"); + + allocInfo.allocationSize = qMax(memReq.size, framesInFlight * m_allocPerUbuf); + allocInfo.memoryTypeIndex = memTypeIndex; + err = m_devFuncs->vkAllocateMemory(m_dev, &allocInfo, nullptr, &m_ubufMem); + if (err != VK_SUCCESS) + qFatal("Failed to allocate uniform buffer memory of size %u: %d", uint(allocInfo.allocationSize), err); + + err = m_devFuncs->vkBindBufferMemory(m_dev, m_ubuf, m_ubufMem, 0); + if (err != VK_SUCCESS) + qFatal("Failed to bind uniform buffer memory: %d", err); + + // Now onto the pipeline. + + VkPipelineCacheCreateInfo pipelineCacheInfo; + memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo)); + pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + err = m_devFuncs->vkCreatePipelineCache(m_dev, &pipelineCacheInfo, nullptr, &m_pipelineCache); + if (err != VK_SUCCESS) + qFatal("Failed to create pipeline cache: %d", err); + + VkDescriptorSetLayoutBinding descLayoutBinding; + memset(&descLayoutBinding, 0, sizeof(descLayoutBinding)); + descLayoutBinding.binding = 0; + descLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + descLayoutBinding.descriptorCount = 1; + descLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + VkDescriptorSetLayoutCreateInfo layoutInfo; + memset(&layoutInfo, 0, sizeof(layoutInfo)); + layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + layoutInfo.bindingCount = 1; + layoutInfo.pBindings = &descLayoutBinding; + err = m_devFuncs->vkCreateDescriptorSetLayout(m_dev, &layoutInfo, nullptr, &m_resLayout); + if (err != VK_SUCCESS) + qFatal("Failed to create descriptor set layout: %d", err); + + VkPipelineLayoutCreateInfo pipelineLayoutInfo; + memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo)); + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &m_resLayout; + err = m_devFuncs->vkCreatePipelineLayout(m_dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout); + if (err != VK_SUCCESS) + qWarning("Failed to create pipeline layout: %d", err); + + VkGraphicsPipelineCreateInfo pipelineInfo; + memset(&pipelineInfo, 0, sizeof(pipelineInfo)); + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + + VkShaderModuleCreateInfo shaderInfo; + memset(&shaderInfo, 0, sizeof(shaderInfo)); + shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + shaderInfo.codeSize = m_vert.size(); + shaderInfo.pCode = reinterpret_cast<const quint32 *>(m_vert.constData()); + VkShaderModule vertShaderModule; + err = m_devFuncs->vkCreateShaderModule(m_dev, &shaderInfo, nullptr, &vertShaderModule); + if (err != VK_SUCCESS) + qFatal("Failed to create vertex shader module: %d", err); + + shaderInfo.codeSize = m_frag.size(); + shaderInfo.pCode = reinterpret_cast<const quint32 *>(m_frag.constData()); + VkShaderModule fragShaderModule; + err = m_devFuncs->vkCreateShaderModule(m_dev, &shaderInfo, nullptr, &fragShaderModule); + if (err != VK_SUCCESS) + qFatal("Failed to create fragment shader module: %d", err); + + VkPipelineShaderStageCreateInfo stageInfo[2]; + memset(&stageInfo, 0, sizeof(stageInfo)); + stageInfo[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stageInfo[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + stageInfo[0].module = vertShaderModule; + stageInfo[0].pName = "main"; + stageInfo[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stageInfo[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + stageInfo[1].module = fragShaderModule; + stageInfo[1].pName = "main"; + pipelineInfo.stageCount = 2; + pipelineInfo.pStages = stageInfo; + + VkVertexInputBindingDescription vertexBinding = { + 0, // binding + 2 * sizeof(float), // stride + VK_VERTEX_INPUT_RATE_VERTEX + }; + VkVertexInputAttributeDescription vertexAttr = { + 0, // location + 0, // binding + VK_FORMAT_R32G32_SFLOAT, // 'vertices' only has 2 floats per vertex + 0 // offset + }; + VkPipelineVertexInputStateCreateInfo vertexInputInfo; + memset(&vertexInputInfo, 0, sizeof(vertexInputInfo)); + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexBinding; + vertexInputInfo.vertexAttributeDescriptionCount = 1; + vertexInputInfo.pVertexAttributeDescriptions = &vertexAttr; + pipelineInfo.pVertexInputState = &vertexInputInfo; + + VkDynamicState dynStates[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamicInfo; + memset(&dynamicInfo, 0, sizeof(dynamicInfo)); + dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicInfo.dynamicStateCount = 2; + dynamicInfo.pDynamicStates = dynStates; + pipelineInfo.pDynamicState = &dynamicInfo; + + VkPipelineViewportStateCreateInfo viewportInfo; + memset(&viewportInfo, 0, sizeof(viewportInfo)); + viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportInfo.viewportCount = viewportInfo.scissorCount = 1; + pipelineInfo.pViewportState = &viewportInfo; + + VkPipelineInputAssemblyStateCreateInfo iaInfo; + memset(&iaInfo, 0, sizeof(iaInfo)); + iaInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + iaInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; + pipelineInfo.pInputAssemblyState = &iaInfo; + + VkPipelineRasterizationStateCreateInfo rsInfo; + memset(&rsInfo, 0, sizeof(rsInfo)); + rsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rsInfo.lineWidth = 1.0f; + pipelineInfo.pRasterizationState = &rsInfo; + + VkPipelineMultisampleStateCreateInfo msInfo; + memset(&msInfo, 0, sizeof(msInfo)); + msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + msInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + pipelineInfo.pMultisampleState = &msInfo; + + VkPipelineDepthStencilStateCreateInfo dsInfo; + memset(&dsInfo, 0, sizeof(dsInfo)); + dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + pipelineInfo.pDepthStencilState = &dsInfo; + + // SrcAlpha, One + VkPipelineColorBlendStateCreateInfo blendInfo; + memset(&blendInfo, 0, sizeof(blendInfo)); + blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + VkPipelineColorBlendAttachmentState blend; + memset(&blend, 0, sizeof(blend)); + blend.blendEnable = true; + blend.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + blend.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + blend.colorBlendOp = VK_BLEND_OP_ADD; + blend.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + blend.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + blend.alphaBlendOp = VK_BLEND_OP_ADD; + blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT + | VK_COLOR_COMPONENT_A_BIT; + blendInfo.attachmentCount = 1; + blendInfo.pAttachments = &blend; + pipelineInfo.pColorBlendState = &blendInfo; + + pipelineInfo.layout = m_pipelineLayout; + + pipelineInfo.renderPass = m_renderPass; + + err = m_devFuncs->vkCreateGraphicsPipelines(m_dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline); + + m_devFuncs->vkDestroyShaderModule(m_dev, vertShaderModule, nullptr); + m_devFuncs->vkDestroyShaderModule(m_dev, fragShaderModule, nullptr); + + if (err != VK_SUCCESS) + qFatal("Failed to create graphics pipeline: %d", err); + + // Now just need some descriptors. + VkDescriptorPoolSize descPoolSizes[] = { + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1 } + }; + VkDescriptorPoolCreateInfo descPoolInfo; + memset(&descPoolInfo, 0, sizeof(descPoolInfo)); + descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + descPoolInfo.flags = 0; // won't use vkFreeDescriptorSets + descPoolInfo.maxSets = 1; + descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]); + descPoolInfo.pPoolSizes = descPoolSizes; + err = m_devFuncs->vkCreateDescriptorPool(m_dev, &descPoolInfo, nullptr, &m_descriptorPool); + if (err != VK_SUCCESS) + qFatal("Failed to create descriptor pool: %d", err); + + VkDescriptorSetAllocateInfo descAllocInfo; + memset(&descAllocInfo, 0, sizeof(descAllocInfo)); + descAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + descAllocInfo.descriptorPool = m_descriptorPool; + descAllocInfo.descriptorSetCount = 1; + descAllocInfo.pSetLayouts = &m_resLayout; + err = m_devFuncs->vkAllocateDescriptorSets(m_dev, &descAllocInfo, &m_ubufDescriptor); + if (err != VK_SUCCESS) + qFatal("Failed to allocate descriptor set"); + + VkWriteDescriptorSet writeInfo; + memset(&writeInfo, 0, sizeof(writeInfo)); + writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + writeInfo.dstSet = m_ubufDescriptor; + writeInfo.dstBinding = 0; + writeInfo.descriptorCount = 1; + writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + VkDescriptorBufferInfo bufInfo; + bufInfo.buffer = m_ubuf; + bufInfo.offset = 0; // dynamic offset is used so this is ignored + bufInfo.range = UBUF_SIZE; + writeInfo.pBufferInfo = &bufInfo; + + m_devFuncs->vkUpdateDescriptorSets(m_dev, 1, &writeInfo, 0, nullptr); + return true; +} + +void CustomTextureNode::sync() +{ + m_dpr = m_window->effectiveDevicePixelRatio(); + const QSize newSize = m_window->size() * m_dpr; + bool needsNew = false; + + if (!m_initialized) { + prepareShader(VertexStage); + prepareShader(FragmentStage); + initialize(); + m_initialized = true; + } + + if (!texture()) + needsNew = true; + + if (newSize != m_size) { + needsNew = true; + m_size = newSize; + } + + if (needsNew) { + delete texture(); + freeTexture(); + buildTexture(m_size); + QSGTexture *wrapper = m_window->createTextureFromNativeObject(QQuickWindow::NativeObjectTexture, + &m_texture, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + m_size); + setTexture(wrapper); + } + + m_t = float(static_cast<CustomTextureItem *>(m_item)->t()); +} + +void CustomTextureNode::render() +{ + if (!m_initialized) + return; + + VkResult err = VK_SUCCESS; + + uint currentFrameSlot = m_window->graphicsStateInfo().currentFrameSlot; + VkDeviceSize ubufOffset = currentFrameSlot * m_allocPerUbuf; + void *p = nullptr; + err = m_devFuncs->vkMapMemory(m_dev, m_ubufMem, ubufOffset, m_allocPerUbuf, 0, &p); + if (err != VK_SUCCESS || !p) + qFatal("Failed to map uniform buffer memory: %d", err); + float t = m_t; + memcpy(p, &t, 4); + m_devFuncs->vkUnmapMemory(m_dev, m_ubufMem); + + VkClearValue clearColor = {{ {0, 0, 0, 1} }}; + + VkRenderPassBeginInfo rpBeginInfo = {}; + rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + rpBeginInfo.renderPass = m_renderPass; + rpBeginInfo.framebuffer = m_textureFramebuffer; + rpBeginInfo.renderArea.extent.width = m_size.width(); + rpBeginInfo.renderArea.extent.height = m_size.height(); + rpBeginInfo.clearValueCount = 1; + rpBeginInfo.pClearValues = &clearColor; + + QSGRendererInterface *rif = m_window->rendererInterface(); + VkCommandBuffer cmdBuf = *reinterpret_cast<VkCommandBuffer *>( + rif->getResource(m_window, QSGRendererInterface::CommandListResource)); + + m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + + m_devFuncs->vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline); + + VkDeviceSize vbufOffset = 0; + m_devFuncs->vkCmdBindVertexBuffers(cmdBuf, 0, 1, &m_vbuf, &vbufOffset); + + uint32_t dynamicOffset = m_allocPerUbuf * currentFrameSlot; + m_devFuncs->vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, + &m_ubufDescriptor, 1, &dynamicOffset); + + VkViewport vp = { 0, 0, float(m_size.width()), float(m_size.height()), 0.0f, 1.0f }; + m_devFuncs->vkCmdSetViewport(cmdBuf, 0, 1, &vp); + VkRect2D scissor = { { 0, 0 }, { uint32_t(m_size.width()), uint32_t(m_size.height()) } }; + m_devFuncs->vkCmdSetScissor(cmdBuf, 0, 1, &scissor); + + m_devFuncs->vkCmdDraw(cmdBuf, 4, 1, 0, 0); + m_devFuncs->vkCmdEndRenderPass(cmdBuf); + + // Memory barrier before the texture can be used as a source. + // Since we are not using a sub-pass, we have to do this explicitly. + + VkImageMemoryBarrier imageTransitionBarrier = {}; + imageTransitionBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + imageTransitionBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + imageTransitionBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + imageTransitionBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + imageTransitionBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageTransitionBarrier.image = m_texture; + imageTransitionBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + imageTransitionBarrier.subresourceRange.levelCount = imageTransitionBarrier.subresourceRange.layerCount = 1; + + m_devFuncs->vkCmdPipelineBarrier(cmdBuf, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, 0, nullptr, 0, nullptr, + 1, &imageTransitionBarrier); +} + +void CustomTextureNode::prepareShader(Stage stage) +{ + QString filename; + if (stage == VertexStage) { + filename = QLatin1String(":/scenegraph/vulkantextureimport/squircle.vert.spv"); + } else { + Q_ASSERT(stage == FragmentStage); + filename = QLatin1String(":/scenegraph/vulkantextureimport/squircle.frag.spv"); + } + QFile f(filename); + if (!f.open(QIODevice::ReadOnly)) + qFatal("Failed to read shader %s", qPrintable(filename)); + + const QByteArray contents = f.readAll(); + + if (stage == VertexStage) { + m_vert = contents; + Q_ASSERT(!m_vert.isEmpty()); + } else { + m_frag = contents; + Q_ASSERT(!m_frag.isEmpty()); + } +} + +#include "vulkantextureimport.moc" diff --git a/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.h b/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.h new file mode 100644 index 0000000000..f604faf66f --- /dev/null +++ b/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VULKANTEXTUREIMPORT_H +#define VULKANTEXTUREIMPORT_H + +#include <QtQuick/QQuickItem> + +class CustomTextureNode; + +//! [1] +class CustomTextureItem : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged) + +public: + CustomTextureItem(); + + qreal t() const { return m_t; } + void setT(qreal t); + +signals: + void tChanged(); + +protected: + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + +private slots: + void invalidateSceneGraph(); + +private: + void releaseResources() override; + + CustomTextureNode *m_node = nullptr; + qreal m_t = 0; +}; +//! [1] + +#endif // VULKANTEXTUREIMPORT_H diff --git a/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.pro b/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.pro new file mode 100644 index 0000000000..f2f78a6ed3 --- /dev/null +++ b/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.pro @@ -0,0 +1,12 @@ +!qtConfig(vulkan): error("This example requires Qt built with Vulkan support") + +QT += qml quick + +HEADERS += vulkantextureimport.h +SOURCES += vulkantextureimport.cpp main.cpp +RESOURCES += vulkantextureimport.qrc + + + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/vulkantextureimport +INSTALLS += target diff --git a/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.qrc b/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.qrc new file mode 100644 index 0000000000..a1aec6c950 --- /dev/null +++ b/examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/scenegraph/vulkantextureimport"> + <file>main.qml</file> + <file>squircle.vert.spv</file> + <file>squircle.frag.spv</file> + </qresource> +</RCC> diff --git a/src/3rdparty/masm/assembler/LinkBuffer.h b/src/3rdparty/masm/assembler/LinkBuffer.h index 632d1fdbc4..ba57564a1d 100644 --- a/src/3rdparty/masm/assembler/LinkBuffer.h +++ b/src/3rdparty/masm/assembler/LinkBuffer.h @@ -209,9 +209,9 @@ public: // complete generation of the code. Alternatively, call // finalizeCodeWithoutDisassembly() directly if you have your own way of // displaying disassembly. - + inline CodeRef finalizeCodeWithoutDisassembly(); - inline CodeRef finalizeCodeWithDisassembly(const char *jitKind, const char* format, ...) WTF_ATTRIBUTE_PRINTF(3, 4); + inline CodeRef finalizeCodeWithDisassembly(const char *jitKind, const char* func); CodePtr trampolineAt(Label label) { @@ -267,9 +267,9 @@ protected: #endif }; -#define FINALIZE_CODE_IF(condition, linkBufferReference, jitKind, dataLogFArgumentsForHeading) \ +#define FINALIZE_CODE_IF(condition, linkBufferReference, jitKind, func) \ (UNLIKELY((condition)) \ - ? ((linkBufferReference).finalizeCodeWithDisassembly (jitKind, dataLogFArgumentsForHeading)) \ + ? ((linkBufferReference).finalizeCodeWithDisassembly (jitKind, func)) \ : (linkBufferReference).finalizeCodeWithoutDisassembly()) // Use this to finalize code, like so: @@ -288,11 +288,11 @@ protected: // Note that the dataLogFArgumentsForHeading are only evaluated when showDisassembly // is true, so you can hide expensive disassembly-only computations inside there. -#define FINALIZE_CODE(linkBufferReference, jitKind, dataLogFArgumentsForHeading) \ - FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, jitKind, dataLogFArgumentsForHeading) +#define FINALIZE_CODE(linkBufferReference, jitKind, func) \ + FINALIZE_CODE_IF(Options::showDisassembly(), linkBufferReference, jitKind, func) -#define FINALIZE_DFG_CODE(linkBufferReference, jitKind, dataLogFArgumentsForHeading) \ - FINALIZE_CODE_IF((Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, jitKind, dataLogFArgumentsForHeading) +#define FINALIZE_DFG_CODE(linkBufferReference, jitKind, func) \ + FINALIZE_CODE_IF((Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, jitKind, func) template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator> @@ -304,23 +304,19 @@ inline typename LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::Code } template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator> -inline typename LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::CodeRef LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::finalizeCodeWithDisassembly(const char *jitKind, const char* format, ...) +inline typename LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::CodeRef LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::finalizeCodeWithDisassembly(const char *jitKind, const char* func) { ASSERT(Options::showDisassembly() || Options::showDFGDisassembly()); CodeRef result = finalizeCodeWithoutDisassembly(); - dataLogF("Generated %s code for ", jitKind); - va_list argList; - va_start(argList, format); - WTF::dataLogFV(format, argList); - va_end(argList); + dataLogF("Generated %s code for function %s:", jitKind, func); dataLogF( #if OS(WINDOWS) - ": Code at [0x%p, 0x%p):", + " Code at [0x%p, 0x%p):", #else - ": Code at [%p, %p):", + " Code at [%p, %p):", #endif result.code().executableAddress(), static_cast<char*>(result.code().executableAddress()) + result.size()); disassemble(result.code(), m_size, " ", WTF::dataFile()); diff --git a/src/plugins/scenegraph/d3d12/d3d12.pro b/src/plugins/scenegraph/d3d12/d3d12.pro index 9cca5458ee..7192efe449 100644 --- a/src/plugins/scenegraph/d3d12/d3d12.pro +++ b/src/plugins/scenegraph/d3d12/d3d12.pro @@ -6,6 +6,18 @@ PLUGIN_TYPE = scenegraph PLUGIN_CLASS_NAME = QSGD3D12Adaptation load(qt_plugin) +TRACEPOINT_PROVIDER = $$PWD/d3d12.tracepoints +CONFIG += qt_tracepoints +debug_and_release { + CONFIG(debug, debug|release) { + INCLUDEPATH += $$OUT_PWD/../../../quick/.tracegen/debug + } else { + INCLUDEPATH += $$OUT_PWD/../../../quick/.tracegen/release + } +} else { + INCLUDEPATH += $$OUT_PWD/../../../quick/.tracegen/ +} + QMAKE_TARGET_PRODUCT = "Qt Quick D3D12 Renderer (Qt $$QT_VERSION)" QMAKE_TARGET_DESCRIPTION = "Quick D3D12 Renderer for Qt." diff --git a/src/plugins/scenegraph/d3d12/d3d12.tracepoints b/src/plugins/scenegraph/d3d12/d3d12.tracepoints new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/plugins/scenegraph/d3d12/d3d12.tracepoints diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp index 4a6894e69e..79ffee7689 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12renderloop.cpp @@ -49,6 +49,8 @@ #include <QGuiApplication> #include <QScreen> +#include <qtquick_tracepoints_p.h> + QT_BEGIN_NAMESPACE // NOTE: Avoid categorized logging. It is slow. @@ -403,6 +405,8 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) return; } + Q_TRACE_SCOPE(QSG_renderWindow); + if (!data.grabOnly) wd->flushFrameSynchronousEvents(); @@ -412,14 +416,17 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) if (profileFrames) renderTimer.start(); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame); + Q_TRACE(QSG_polishItems_entry); wd->polishItems(); if (profileFrames) polishTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_polishItems_exit); Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame, QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphPolishPolish); + Q_TRACE(QSG_sync_entry); emit window->afterAnimating(); @@ -462,15 +469,19 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) if (profileFrames) syncTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_sync_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSync); + Q_TRACE(QSG_render_entry); wd->renderSceneGraph(window->size()); if (profileFrames) renderTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopRender); + Q_TRACE(QSG_swap_entry); if (!data.grabOnly) { // The engine is able to have multiple frames in flight. This in effect is @@ -497,6 +508,7 @@ void QSGD3D12RenderLoop::renderWindow(QQuickWindow *window) qint64 swapTime = 0; if (profileFrames) swapTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_swap_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSwap); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp index 4302a9119b..91a35627ea 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp +++ b/src/plugins/scenegraph/d3d12/qsgd3d12threadedrenderloop.cpp @@ -52,6 +52,8 @@ #include <QQueue> #include <QGuiApplication> +#include <qtquick_tracepoints_p.h> + QT_BEGIN_NAMESPACE // NOTE: Avoid categorized logging. It is slow. @@ -560,11 +562,13 @@ void QSGD3D12RenderThread::sync(bool inExpose) void QSGD3D12RenderThread::syncAndRender() { + Q_TRACE_SCOPE(QSG_syncAndRender); if (Q_UNLIKELY(debug_time())) { sinceLastTime = threadTimer.nsecsElapsed(); threadTimer.start(); } Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame); + Q_TRACE(QSG_sync_entry); QElapsedTimer waitTimer; waitTimer.start(); @@ -587,6 +591,7 @@ void QSGD3D12RenderThread::syncAndRender() if (Q_UNLIKELY(debug_time())) syncTime = threadTimer.nsecsElapsed(); #endif + Q_TRACE(QSG_sync_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSync); @@ -598,6 +603,7 @@ void QSGD3D12RenderThread::syncAndRender() msleep(waitTime); return; } + Q_TRACE(QSG_render_entry); if (Q_UNLIKELY(debug_loop())) qDebug("RT - rendering started"); @@ -623,8 +629,10 @@ void QSGD3D12RenderThread::syncAndRender() wd->renderSceneGraph(engine->windowSize()); if (Q_UNLIKELY(debug_time())) renderTime = threadTimer.nsecsElapsed(); + Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopRender); + Q_TRACE(QSG_swap_entry); // The engine is able to have multiple frames in flight. This in effect is // similar to BufferQueueingOpenGL. Provide an env var to force the @@ -641,8 +649,10 @@ void QSGD3D12RenderThread::syncAndRender() // blockOnEachFrame is not used, but emit it for compatibility. wd->fireFrameSwapped(); } else { + Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSync, 1); + Q_TRACE(QSG_swap_entry); if (Q_UNLIKELY(debug_loop())) qDebug("RT - window not ready, skipping render"); } @@ -664,6 +674,7 @@ void QSGD3D12RenderThread::syncAndRender() int((renderTime - syncTime) / 1000000), int(threadTimer.elapsed() - renderTime / 1000000)); + Q_TRACE(QSG_swap_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSwap); @@ -1103,6 +1114,8 @@ void QSGD3D12ThreadedRenderLoop::polishAndSync(WindowData *w, bool inExpose) return; } + Q_TRACE_SCOPE(QSG_polishAndSync); + // Flush pending touch events. QQuickWindowPrivate::get(window)->flushFrameSynchronousEvents(); // The delivery of the event might have caused the window to stop rendering @@ -1120,14 +1133,17 @@ void QSGD3D12ThreadedRenderLoop::polishAndSync(WindowData *w, bool inExpose) if (Q_UNLIKELY(debug_time())) timer.start(); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync); + Q_TRACE(QSG_polishItems_entry); QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); wd->polishItems(); if (Q_UNLIKELY(debug_time())) polishTime = timer.nsecsElapsed(); + Q_TRACE(QSG_polishItems_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncPolish); + Q_TRACE(QSG_wait_entry); w->updateDuringSync = false; @@ -1144,8 +1160,11 @@ void QSGD3D12ThreadedRenderLoop::polishAndSync(WindowData *w, bool inExpose) qDebug("polishAndSync - wait for sync"); if (Q_UNLIKELY(debug_time())) waitTime = timer.nsecsElapsed(); + Q_TRACE(QSG_wait_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncWait); + Q_TRACE(QSG_sync_entry); + w->thread->waitCondition.wait(&w->thread->mutex); lockedForSync = false; w->thread->mutex.unlock(); @@ -1154,8 +1173,10 @@ void QSGD3D12ThreadedRenderLoop::polishAndSync(WindowData *w, bool inExpose) if (Q_UNLIKELY(debug_time())) syncTime = timer.nsecsElapsed(); + Q_TRACE(QSG_sync_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncSync); + Q_TRACE(QSG_animations_entry); if (!animationTimer && anim->isRunning()) { if (Q_UNLIKELY(debug_loop())) @@ -1177,6 +1198,7 @@ void QSGD3D12ThreadedRenderLoop::polishAndSync(WindowData *w, bool inExpose) << ", animations=" << (timer.nsecsElapsed() - syncTime) / 1000000 << " - (on gui thread) " << window; + Q_TRACE(QSG_animations_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncAnimations); } diff --git a/src/plugins/scenegraph/openvg/openvg.pro b/src/plugins/scenegraph/openvg/openvg.pro index 43c2636343..7f9f4bfec5 100644 --- a/src/plugins/scenegraph/openvg/openvg.pro +++ b/src/plugins/scenegraph/openvg/openvg.pro @@ -6,6 +6,18 @@ PLUGIN_TYPE = scenegraph PLUGIN_CLASS_NAME = QSGOpenVGAdaptation load(qt_plugin) +TRACEPOINT_PROVIDER = $$PWD/openvg.tracepoints +CONFIG += qt_tracepoints +debug_and_release { + CONFIG(debug, debug|release) { + INCLUDEPATH += $$OUT_PWD/../../../quick/.tracegen/debug + } else { + INCLUDEPATH += $$OUT_PWD/../../../quick/.tracegen/release + } +} else { + INCLUDEPATH += $$OUT_PWD/../../../quick/.tracegen/ +} + QMAKE_TARGET_PRODUCT = "Qt Quick OpenVG Renderer (Qt $$QT_VERSION)" QMAKE_TARGET_DESCRIPTION = "Quick OpenVG Renderer for Qt." diff --git a/src/plugins/scenegraph/openvg/openvg.tracepoints b/src/plugins/scenegraph/openvg/openvg.tracepoints new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/src/plugins/scenegraph/openvg/openvg.tracepoints diff --git a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp index 85651ece9d..90ccab2682 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgrenderloop.cpp @@ -47,6 +47,8 @@ #include <private/qquickwindow_p.h> #include <private/qquickprofiler_p.h> +#include <qtquick_tracepoints_p.h> + #include "qopenvgcontext_p.h" QT_BEGIN_NAMESPACE @@ -171,6 +173,8 @@ void QSGOpenVGRenderLoop::renderWindow(QQuickWindow *window) if (!cd->isRenderable() || !m_windows.contains(window)) return; + Q_TRACE_SCOPE(QSG_renderWindow); + WindowData &data = const_cast<WindowData &>(m_windows[window]); if (vg == nullptr) { @@ -198,14 +202,17 @@ void QSGOpenVGRenderLoop::renderWindow(QQuickWindow *window) if (profileFrames) renderTimer.start(); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame); + Q_TRACE(QSG_polishItems_entry); cd->polishItems(); if (profileFrames) polishTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_polishItems_exit); Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame, QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphPolishPolish); + Q_TRACE(QSG_sync_entry); emit window->afterAnimating(); @@ -214,8 +221,10 @@ void QSGOpenVGRenderLoop::renderWindow(QQuickWindow *window) if (profileFrames) syncTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_sync_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSync); + Q_TRACE(QSG_render_entry); // setup coordinate system for window vgSeti(VG_MATRIX_MODE, VG_MATRIX_PATH_USER_TO_SURFACE); @@ -227,8 +236,10 @@ void QSGOpenVGRenderLoop::renderWindow(QQuickWindow *window) if (profileFrames) renderTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopRender); + Q_TRACE(QSG_swap_entry); if (data.grabOnly) { grabContent = vg->readFramebuffer(window->size() * window->effectiveDevicePixelRatio()); @@ -243,6 +254,7 @@ void QSGOpenVGRenderLoop::renderWindow(QQuickWindow *window) qint64 swapTime = 0; if (profileFrames) swapTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_swap_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSwap); diff --git a/src/qml/jit/qv4assemblercommon.cpp b/src/qml/jit/qv4assemblercommon.cpp index e75f35a665..cc8af723b2 100644 --- a/src/qml/jit/qv4assemblercommon.cpp +++ b/src/qml/jit/qv4assemblercommon.cpp @@ -146,7 +146,7 @@ void PlatformAssemblerCommon::link(Function *function, const char *jitKind) // We use debugAddress here because it's actually for debugging and hidden behind an // environment variable. const QByteArray name = Function::prettyName(function, linkBuffer.debugAddress()).toUtf8(); - codeRef = linkBuffer.finalizeCodeWithDisassembly(jitKind, "function %s", name.constData()); + codeRef = linkBuffer.finalizeCodeWithDisassembly(jitKind, name.constData()); WTF::setDataFile(stderr); printDisassembledOutputWithCalls(buf.data(), functions); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index e143523165..fb103d492d 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -434,7 +434,7 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) Function *function = frame->v4Function; Q_TRACE_SCOPE(QQmlV4_function_call, engine, function->name()->toQString(), - function->compilationUnit->fileName(), + function->executableCompilationUnit()->fileName(), function->compiledFunction->location.line, function->compiledFunction->location.column); Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 112e5b558a..162adf8364 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -54,6 +54,8 @@ #include <private/qv4variantobject_p.h> #include <private/qv4jscall_p.h> +#include <qtqml_tracepoints_p.h> + #include <QVariant> #include <QtCore/qdebug.h> #include <QVector> @@ -182,6 +184,8 @@ void QQmlBinding::update(QQmlPropertyData::WriteFlags flags) if (canUseAccessor()) flags.setFlag(QQmlPropertyData::BypassInterceptor); + Q_TRACE_SCOPE(QQmlBinding, engine, function() ? function()->name()->toQString() : QString(), + sourceLocation().sourceFile, sourceLocation().line, sourceLocation().column); QQmlBindingProfiler prof(QQmlEnginePrivate::get(engine)->profiler, function()); doUpdate(watcher, flags, scope); diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index ff01e737ca..b347bb3829 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -60,6 +60,7 @@ #include <QtCore/qdebug.h> +#include <qtqml_tracepoints_p.h> QT_BEGIN_NAMESPACE @@ -352,6 +353,10 @@ void QQmlBoundSignal_callback(QQmlNotifierEndpoint *e, void **a) QQmlEngine *engine; if (s->m_expression && (engine = s->m_expression->engine())) { + Q_TRACE_SCOPE(QQmlHandlingSignal, engine, + s->m_expression->function() ? s->m_expression->function()->name()->toQString() : QString(), + s->m_expression->sourceLocation().sourceFile, s->m_expression->sourceLocation().line, + s->m_expression->sourceLocation().column); QQmlHandlingSignalProfiler prof(QQmlEnginePrivate::get(engine)->profiler, s->m_expression); s->m_expression->evaluate(a); if (s->m_expression && s->m_expression->hasError()) { diff --git a/src/qml/qml/qqmldatablob.cpp b/src/qml/qml/qqmldatablob.cpp index 750fc6de50..1ab6002f0a 100644 --- a/src/qml/qml/qqmldatablob.cpp +++ b/src/qml/qml/qqmldatablob.cpp @@ -45,6 +45,8 @@ #include <QtQml/qqmlengine.h> +#include <qtqml_tracepoints_p.h> + #ifdef DATABLOB_DEBUG #define ASSERT_CALLBACK() do { if (!m_typeLoader || !m_typeLoader->m_thread->isThisThread()) qFatal("QQmlDataBlob: An API call was made outside a callback"); } while (false) #else @@ -559,6 +561,7 @@ void QQmlDataBlob::notifyAllWaitingOnMe() void QQmlDataBlob::notifyComplete(QQmlDataBlob *blob) { Q_ASSERT(blob->status() == Error || blob->status() == Complete); + Q_TRACE_SCOPE(QQmlCompiling, blob->url()); QQmlCompilingProfiler prof(typeLoader()->profiler(), blob); m_inCallback = true; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index e0131c0a98..989c3fe4b0 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -51,6 +51,8 @@ #include <QtQml/qqmlextensioninterface.h> #include <QtQml/qqmlfile.h> +#include <qtqml_tracepoints_p.h> + #include <QtCore/qdir.h> #include <QtCore/qdiriterator.h> #include <QtCore/qfile.h> @@ -436,6 +438,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName) void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeData &d) { + Q_TRACE_SCOPE(QQmlCompiling, blob->url()); QQmlCompilingProfiler prof(profiler(), blob); blob->m_inCallback = true; @@ -455,6 +458,7 @@ void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeD void QQmlTypeLoader::setCachedUnit(QQmlDataBlob *blob, const QV4::CompiledData::Unit *unit) { + Q_TRACE_SCOPE(QQmlCompiling, blob->url()); QQmlCompilingProfiler prof(profiler(), blob); blob->m_inCallback = true; diff --git a/src/qml/qtqml.tracepoints b/src/qml/qtqml.tracepoints index 841748f201..1043d3bae3 100644 --- a/src/qml/qtqml.tracepoints +++ b/src/qml/qtqml.tracepoints @@ -6,9 +6,16 @@ class CompilationUnit; class Object; } // CompiledData } // QV4 +class QQmlEngine; } QQmlObjectCreator_createInstance_entry(const QV4::CompiledData::CompilationUnit *compilationUnit, const QV4::CompiledData::Object *object, const QUrl &url) QQmlObjectCreator_createInstance_exit(const QString &typeName) +QQmlCompiling_entry(const QUrl &url) +QQmlCompiling_exit() QQmlV4_function_call_entry(const QV4::ExecutionEngine *engine, const QString &function, const QString &fileName, int line, int column) QQmlV4_function_call_exit() +QQmlBinding_entry(const QQmlEngine *engine, const QString &function, const QString &fileName, int line, int column) +QQmlBinding_exit() +QQmlHandlingSignal_entry(const QQmlEngine *engine, const QString &function, const QString &fileName, int line, int column) +QQmlHandlingSignal_exit() diff --git a/src/qmlworkerscript/qv4serialize.cpp b/src/qmlworkerscript/qv4serialize.cpp index a5e62d3e35..f0a644a8b8 100644 --- a/src/qmlworkerscript/qv4serialize.cpp +++ b/src/qmlworkerscript/qv4serialize.cpp @@ -81,6 +81,7 @@ enum Type { WorkerDate, WorkerRegexp, WorkerListModel, + WorkerUrl, #if QT_CONFIG(qml_sequence_object) WorkerSequence #endif @@ -142,10 +143,29 @@ static inline void *popPtr(const char *&data) return rv; } +#define ALIGN(size) (((size) + 3) & ~3) +static inline void serializeString(QByteArray &data, const QString &str, Type type) +{ + int length = str.length(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + int utf16size = ALIGN(length * sizeof(quint16)); + + reserve(data, utf16size + sizeof(quint32)); + push(data, valueheader(type, length)); + + int offset = data.size(); + data.resize(data.size() + utf16size); + char *buffer = data.data() + offset; + + memcpy(buffer, str.constData(), length*sizeof(QChar)); +} + // XXX TODO: Check that worker script is exception safe in the case of // serialization/deserialization failures -#define ALIGN(size) (((size) + 3) & ~3) void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine *engine) { QV4::Scope scope(engine); @@ -159,22 +179,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine } else if (v.isBoolean()) { push(data, valueheader(v.booleanValue() == true ? WorkerTrue : WorkerFalse)); } else if (v.isString()) { - const QString &qstr = v.toQString(); - int length = qstr.length(); - if (length > 0xFFFFFF) { - push(data, valueheader(WorkerUndefined)); - return; - } - int utf16size = ALIGN(length * sizeof(quint16)); - - reserve(data, utf16size + sizeof(quint32)); - push(data, valueheader(WorkerString, length)); - - int offset = data.size(); - data.resize(data.size() + utf16size); - char *buffer = data.data() + offset; - - memcpy(buffer, qstr.constData(), length*sizeof(QChar)); + serializeString(data, v.toQString(), WorkerString); } else if (v.as<FunctionObject>()) { // XXX TODO: Implement passing function objects between the main and // worker scripts @@ -259,6 +264,11 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine return; } #endif + const QVariant variant = engine->toVariant(v, QMetaType::QUrl, false); + if (variant.userType() == QMetaType::QUrl) { + serializeString(data, variant.value<QUrl>().toString(), WorkerUrl); + return; + } // regular object QV4::ScopedValue val(scope, v); @@ -340,11 +350,14 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) case WorkerFalse: return QV4::Encode(false); case WorkerString: + case WorkerUrl: { quint32 size = headersize(header); QString qstr((const QChar *)data, size); data += ALIGN(size * sizeof(quint16)); - return QV4::Encode(engine->newString(qstr)); + return (type == WorkerUrl) + ? engine->fromVariant(QVariant::fromValue(QUrl(qstr))) + : Encode(engine->newString(qstr)); } case WorkerFunction: Q_ASSERT(!"Unreachable"); diff --git a/src/quick/qtquick.tracepoints b/src/quick/qtquick.tracepoints new file mode 100644 index 0000000000..f6518444d2 --- /dev/null +++ b/src/quick/qtquick.tracepoints @@ -0,0 +1,48 @@ +QSG_renderWindow_entry() +QSG_renderWindow_exit() +QSG_renderScene_entry() +QSG_renderScene_exit() +QSG_syncAndRender_entry() +QSG_syncAndRender_exit() +QSG_polishAndSync_entry() +QSG_polishAndSync_exit() +QSG_preprocess_entry() +QSG_preprocess_exit() +QSG_update_entry() +QSG_update_exit() + +QSG_polishItems_entry() +QSG_polishItems_exit() +QSG_sync_entry() +QSG_sync_exit() +QSG_wait_entry() +QSG_wait_exit() +QSG_render_entry() +QSG_render_exit() +QSG_swap_entry() +QSG_swap_exit() +QSG_animations_entry() +QSG_animations_exit() + +QSG_texture_prepare_entry() +QSG_texture_prepare_exit() +QSG_texture_bind_entry() +QSG_texture_bind_exit() +QSG_texture_delete_entry() +QSG_texture_delete_exit() +QSG_texture_upload_entry() +QSG_texture_upload_exit() +QSG_texture_mipmap_entry() +QSG_texture_mipmap_exit() + +QSG_binding_entry() +QSG_binding_exit() +QSG_prepareMaterial_entry() +QSG_prepareMaterial_exit() + +QSGDistanceFieldGlyphCache_update_entry(int count) +QSGDistanceFieldGlyphCache_update_exit() +QSGDistanceFieldGlyphCache_glyphRender_entry() +QSGDistanceFieldGlyphCache_glyphRender_exit() +QSGDistanceFieldGlyphCache_glyphStore_entry() +QSGDistanceFieldGlyphCache_glyphStore_exit() diff --git a/src/quick/quick.pro b/src/quick/quick.pro index 700f794af4..2f22fb2c4a 100644 --- a/src/quick/quick.pro +++ b/src/quick/quick.pro @@ -4,6 +4,9 @@ QT = core-private gui-private qml-private qmlmodels-private qtConfig(qml-network): \ QT_PRIVATE += network +TRACEPOINT_PROVIDER = $$PWD/qtquick.tracepoints +CONFIG += qt_tracepoints + DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES msvc:DEFINES *= _CRT_SECURE_NO_WARNINGS solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp index c97dcb9326..c010e0cae5 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -52,6 +52,8 @@ #include <QtGui/QBackingStore> +#include <qtquick_tracepoints_p.h> + QT_BEGIN_NAMESPACE QSGSoftwareRenderLoop::QSGSoftwareRenderLoop() @@ -133,20 +135,25 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) if (!m_windows.contains(window)) return; } + + Q_TRACE_SCOPE(QSG_renderWindow) QElapsedTimer renderTimer; qint64 renderTime = 0, syncTime = 0, polishTime = 0; bool profileFrames = QSG_RASTER_LOG_TIME_RENDERLOOP().isDebugEnabled(); if (profileFrames) renderTimer.start(); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame); + Q_TRACE(QSG_polishItems_entry); cd->polishItems(); if (profileFrames) polishTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_polishItems_exit); Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame, QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphPolishPolish); + Q_TRACE(QSG_sync_entry); emit window->afterAnimating(); @@ -155,8 +162,10 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) if (profileFrames) syncTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_sync_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSync); + Q_TRACE(QSG_render_entry); //Tell the renderer about the windows backing store auto softwareRenderer = static_cast<QSGSoftwareRenderer*>(cd->renderer); @@ -167,8 +176,10 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) if (profileFrames) renderTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopRender); + Q_TRACE(QSG_swap_entry); if (data.grabOnly) { grabContent = m_backingStores[window]->handle()->toImage(); @@ -187,6 +198,7 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) qint64 swapTime = 0; if (profileFrames) swapTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_swap_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSwap); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp index c6b463bb02..f07cc28827 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp @@ -60,6 +60,8 @@ #include <QtGui/QBackingStore> #include <QtQuick/QQuickWindow> +#include <qtquick_tracepoints_p.h> + QT_BEGIN_NAMESPACE // Passed from the RL to the RT when a window is removed obscured and should be @@ -457,6 +459,8 @@ void QSGSoftwareRenderThread::sync(bool inExpose) void QSGSoftwareRenderThread::syncAndRender() { + Q_TRACE_SCOPE(QSG_syncAndRender); + Q_TRACE(QSG_sync_entry); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame); QElapsedTimer waitTimer; @@ -475,6 +479,7 @@ void QSGSoftwareRenderThread::syncAndRender() if (syncRequested) sync(exposeRequested); + Q_TRACE(QSG_sync_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSync); @@ -487,6 +492,7 @@ void QSGSoftwareRenderThread::syncAndRender() } qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - rendering started"); + Q_TRACE(QSG_render_entry); if (rtAnim->isRunning()) { wd->animationController->lock(); @@ -502,8 +508,10 @@ void QSGSoftwareRenderThread::syncAndRender() softwareRenderer->setBackingStore(backingStore); wd->renderSceneGraph(exposedWindow->size()); + Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopRender); + Q_TRACE(QSG_swap_entry); if (softwareRenderer && (!wd->customRenderStage || !wd->customRenderStage->swap())) backingStore->flush(softwareRenderer->flushRegion()); @@ -519,8 +527,10 @@ void QSGSoftwareRenderThread::syncAndRender() wd->fireFrameSwapped(); } else { + Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSync, 1); + Q_TRACE(QSG_swap_entry); qCDebug(QSG_RASTER_LOG_RENDERLOOP, "RT - window not ready, skipping render"); } @@ -532,6 +542,7 @@ void QSGSoftwareRenderThread::syncAndRender() mutex.unlock(); } + Q_TRACE(QSG_swap_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSwap); } @@ -947,13 +958,18 @@ void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop: return; } + Q_TRACE_SCOPE(QSG_polishAndSync); + + Q_TRACE(QSG_polishItems_entry); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync); QQuickWindowPrivate *wd = QQuickWindowPrivate::get(window); wd->polishItems(); + Q_TRACE(QSG_polishItems_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncPolish); + Q_TRACE(QSG_sync_entry); w->updateDuringSync = false; @@ -967,15 +983,19 @@ void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop: qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - wait for sync"); + Q_TRACE(QSG_sync_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncWait); + Q_TRACE(QSG_wait_entry); w->thread->waitCondition.wait(&w->thread->mutex); lockedForSync = false; w->thread->mutex.unlock(); qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - unlock after sync"); + Q_TRACE(QSG_wait_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncSync); + Q_TRACE(QSG_animations_entry); if (!animationTimer && m_anim->isRunning()) { qCDebug(QSG_RASTER_LOG_RENDERLOOP, "polishAndSync - advancing animations"); @@ -987,6 +1007,7 @@ void QSGSoftwareThreadedRenderLoop::polishAndSync(QSGSoftwareThreadedRenderLoop: w->window->requestUpdate(); } + Q_TRACE(QSG_animations_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncAnimations); } diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp index 9ccf483622..4f6efc3b36 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp @@ -62,6 +62,8 @@ #include "qsgopenglvisualizer_p.h" #include "qsgrhivisualizer_p.h" +#include <qtquick_tracepoints_p.h> + #include <algorithm> #ifndef GL_DOUBLE @@ -270,6 +272,7 @@ ShaderManager::Shader *ShaderManager::prepareMaterial(QSGMaterial *material, boo return nullptr; } + Q_TRACE_SCOPE(QSG_prepareMaterial); if (QSG_LOG_TIME_COMPILATION().isDebugEnabled()) qsg_renderer_timer.start(); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame); @@ -332,6 +335,7 @@ ShaderManager::Shader *ShaderManager::prepareMaterialNoRewrite(QSGMaterial *mate return nullptr; } + Q_TRACE_SCOPE(QSG_prepareMaterial); if (QSG_LOG_TIME_COMPILATION().isDebugEnabled()) qsg_renderer_timer.start(); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphContextFrame); diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index 7af932eeb5..90090e1cc0 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -45,6 +45,7 @@ # include <QtGui/QOpenGLFunctions> #endif #include <private/qquickprofiler_p.h> +#include <qtquick_tracepoints_p.h> #include <QtCore/QElapsedTimer> @@ -221,6 +222,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) if (!rootNode()) return; + Q_TRACE_SCOPE(QSG_renderScene); m_is_rendering = true; @@ -235,11 +237,14 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) m_bindable = &bindable; preprocess(); + Q_TRACE(QSG_binding_entry); bindable.bind(); if (profileFrames) bindTime = frameTimer.nsecsElapsed(); + Q_TRACE(QSG_binding_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame, QQuickProfiler::SceneGraphRendererBinding); + Q_TRACE(QSG_render_entry); #if QT_CONFIG(opengl) // Sanity check that attribute registers are disabled @@ -259,6 +264,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) render(); if (profileFrames) renderTime = frameTimer.nsecsElapsed(); + Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRendererFrame, QQuickProfiler::SceneGraphRendererRender); @@ -305,6 +311,8 @@ void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) void QSGRenderer::preprocess() { + Q_TRACE(QSG_preprocess_entry); + m_is_preprocessing = true; QSGRootNode *root = rootNode(); @@ -331,13 +339,16 @@ void QSGRenderer::preprocess() bool profileFrames = QSG_LOG_TIME_RENDERER().isDebugEnabled(); if (profileFrames) preprocessTime = frameTimer.nsecsElapsed(); + Q_TRACE(QSG_preprocess_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame, QQuickProfiler::SceneGraphRendererPreprocess); + Q_TRACE(QSG_update_entry); nodeUpdater()->updateStates(root); if (profileFrames) updatePassTime = frameTimer.nsecsElapsed(); + Q_TRACE(QSG_update_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame, QQuickProfiler::SceneGraphRendererUpdate); diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index f7b07d724a..eab0369be7 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -50,6 +50,8 @@ #include <private/qquickprofiler_p.h> #include <QElapsedTimer> +#include <qtquick_tracepoints_p.h> + QT_BEGIN_NAMESPACE static QElapsedTimer qsg_render_timer; @@ -169,10 +171,13 @@ void QSGDistanceFieldGlyphCache::update() if (m_pendingGlyphs.isEmpty()) return; + Q_TRACE_SCOPE(QSGDistanceFieldGlyphCache_update, m_pendingGlyphs.size()); + bool profileFrames = QSG_LOG_TIME_GLYPH().isDebugEnabled(); if (profileFrames) qsg_render_timer.start(); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphAdaptationLayerFrame); + Q_TRACE(QSGDistanceFieldGlyphCache_glyphRender_entry); QList<QDistanceField> distanceFields; const int pendingGlyphsSize = m_pendingGlyphs.size(); @@ -189,8 +194,11 @@ void QSGDistanceFieldGlyphCache::update() int count = m_pendingGlyphs.size(); if (profileFrames) renderTime = qsg_render_timer.nsecsElapsed(); + + Q_TRACE(QSGDistanceFieldGlyphCache_glyphRender_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphAdaptationLayerFrame, QQuickProfiler::SceneGraphAdaptationLayerGlyphRender); + Q_TRACE(QSGDistanceFieldGlyphCache_glyphStore_entry); m_pendingGlyphs.reset(); @@ -210,6 +218,7 @@ void QSGDistanceFieldGlyphCache::update() int(renderTime / 1000000), int((now - (renderTime / 1000000)))); } + Q_TRACE(QSGDistanceFieldGlyphCache_glyphStore_exit); Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(QQuickProfiler::SceneGraphAdaptationLayerFrame, QQuickProfiler::SceneGraphAdaptationLayerGlyphStore, (qint64)count); diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index fb460f28d6..94f15b55d4 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -60,6 +60,7 @@ #include <QtQuick/private/qsgcontext_p.h> #include <QtQuick/private/qsgrenderer_p.h> #include <private/qquickprofiler_p.h> +#include <qtquick_tracepoints_p.h> #include <private/qsgrhishadereffectnode_p.h> @@ -680,20 +681,25 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) return; } + Q_TRACE_SCOPE(QSG_renderWindow); QElapsedTimer renderTimer; qint64 renderTime = 0, syncTime = 0, polishTime = 0; bool profileFrames = QSG_LOG_TIME_RENDERLOOP().isDebugEnabled(); if (profileFrames) renderTimer.start(); + Q_TRACE(QSG_polishItems_entry); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame); cd->polishItems(); if (profileFrames) polishTime = renderTimer.nsecsElapsed(); + + Q_TRACE(QSG_polishItems_exit); Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame, QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphPolishPolish); + Q_TRACE(QSG_sync_entry); emit window->afterAnimating(); @@ -748,15 +754,20 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) if (profileFrames) syncTime = renderTimer.nsecsElapsed(); + + Q_TRACE(QSG_sync_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSync); + Q_TRACE(QSG_render_entry); cd->renderSceneGraph(window->size(), effectiveOutputSize); if (profileFrames) renderTime = renderTimer.nsecsElapsed(); + Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopRender); + Q_TRACE(QSG_swap_entry); if (data.grabOnly) { const bool alpha = window->format().alphaBufferSize() > 0 && window->color().alpha() != 255; @@ -790,6 +801,8 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) qint64 swapTime = 0; if (profileFrames) swapTime = renderTimer.nsecsElapsed(); + + Q_TRACE(QSG_swap_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSwap); diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index a213c6efd4..4777f46f0a 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -70,6 +70,8 @@ #include <private/qsgrhishadereffectnode_p.h> #include <private/qsgdefaultrendercontext_p.h> +#include <qtquick_tracepoints_p.h> + /* Overall design: @@ -686,7 +688,9 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) sinceLastTime = threadTimer.nsecsElapsed(); threadTimer.start(); } + Q_TRACE_SCOPE(QSG_syncAndRender); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphRenderLoopFrame); + Q_TRACE(QSG_sync_entry); QElapsedTimer waitTimer; waitTimer.start(); @@ -774,6 +778,7 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) if (profileFrames) syncTime = threadTimer.nsecsElapsed(); #endif + Q_TRACE(QSG_sync_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSync); @@ -793,6 +798,8 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- rendering started"); + Q_TRACE(QSG_render_entry); + // RepaintRequest may have been set in pendingUpdate in an // updatePaintNode() invoked from sync(). We are about to do a repaint // right now, so reset the flag. (bits other than RepaintRequest cannot @@ -828,8 +835,10 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) if (profileFrames) renderTime = threadTimer.nsecsElapsed(); + Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopRender); + Q_TRACE(QSG_swap_entry); // With the rhi grabs can only be done by adding a readback and then // blocking in a real frame. The legacy GL path never gets here with @@ -860,10 +869,11 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) if (!grabRequested) d->fireFrameSwapped(); - } else { + Q_TRACE(QSG_render_exit); Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSync, 1); + Q_TRACE(QSG_swap_entry); qCDebug(QSG_LOG_RENDERLOOP, QSG_RT_PAD, "- window not ready, skipping render"); } @@ -887,7 +897,7 @@ void QSGRenderThread::syncAndRender(QImage *grabImage) int((renderTime - syncTime) / 1000000), int(threadTimer.elapsed() - renderTime / 1000000)); - + Q_TRACE(QSG_swap_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphRenderLoopSwap); @@ -1546,7 +1556,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) return; } - + Q_TRACE_SCOPE(QSG_polishAndSync); QElapsedTimer timer; qint64 polishTime = 0; qint64 waitTime = 0; @@ -1555,14 +1565,17 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) if (profileFrames) timer.start(); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishAndSync); + Q_TRACE(QSG_polishItems_entry); QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); d->polishItems(); if (profileFrames) polishTime = timer.nsecsElapsed(); + Q_TRACE(QSG_polishItems_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncPolish); + Q_TRACE(QSG_wait_entry); w->updateDuringSync = false; @@ -1577,8 +1590,11 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) qCDebug(QSG_LOG_RENDERLOOP, "- wait for sync"); if (profileFrames) waitTime = timer.nsecsElapsed(); + Q_TRACE(QSG_wait_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncWait); + Q_TRACE(QSG_sync_entry); + w->thread->waitCondition.wait(&w->thread->mutex); m_lockedForSync = false; w->thread->mutex.unlock(); @@ -1586,8 +1602,10 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) if (profileFrames) syncTime = timer.nsecsElapsed(); + Q_TRACE(QSG_sync_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncSync); + Q_TRACE(QSG_animations_entry); if (m_animation_timer == 0 && m_animation_driver->isRunning()) { qCDebug(QSG_LOG_RENDERLOOP, "- advancing animations"); @@ -1608,6 +1626,7 @@ void QSGThreadedRenderLoop::polishAndSync(Window *w, bool inExpose) << ", animations=" << (timer.nsecsElapsed() - syncTime) / 1000000 << " - (on Gui thread) " << window; + Q_TRACE(QSG_animations_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphPolishAndSync, QQuickProfiler::SceneGraphPolishAndSyncAnimations); } diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index b1c9b71c05..20d7c4557f 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -60,6 +60,8 @@ #include <private/qquickopenglshadereffectnode_p.h> #endif +#include <qtquick_tracepoints_p.h> + QT_BEGIN_NAMESPACE // Single-threaded render loop with a custom animation driver. Like a @@ -392,6 +394,7 @@ bool QSGWindowsRenderLoop::event(QEvent *event) void QSGWindowsRenderLoop::render() { RLDEBUG("render"); + Q_TRACE(QSG_render_entry); bool rendered = false; for (const WindowData &wd : qAsConst(m_windows)) { if (wd.pendingUpdate) { @@ -406,10 +409,13 @@ void QSGWindowsRenderLoop::render() QThread::msleep(m_vsyncDelta); } + Q_TRACE(QSG_render_exit); + if (m_animationDriver->isRunning()) { RLDEBUG("advancing animations"); QSG_LOG_TIME_SAMPLE(time_start); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphWindowsAnimations); + Q_TRACE(QSG_animations_entry); m_animationDriver->advance(); RLDEBUG("animations advanced"); @@ -417,6 +423,7 @@ void QSGWindowsRenderLoop::render() "animations ticked in %dms", int((qsg_render_timer.nsecsElapsed() - time_start)/1000000)); + Q_TRACE(QSG_animations_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphWindowsAnimations, 1); // It is not given that animations triggered another maybeUpdate() @@ -475,15 +482,20 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) if (!windowData(window)) return; + Q_TRACE_SCOPE(QSG_renderWindow); + QSG_LOG_TIME_SAMPLE(time_start); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphPolishFrame); + Q_TRACE(QSG_polishItems_entry); RLDEBUG(" - polishing"); d->polishItems(); QSG_LOG_TIME_SAMPLE(time_polished); + Q_TRACE(QSG_polishItems_exit); Q_QUICK_SG_PROFILE_SWITCH(QQuickProfiler::SceneGraphPolishFrame, QQuickProfiler::SceneGraphRenderLoopFrame, QQuickProfiler::SceneGraphPolishPolish); + Q_TRACE(QSG_sync_entry); emit window->afterAnimating(); @@ -491,17 +503,22 @@ void QSGWindowsRenderLoop::renderWindow(QQuickWindow *window) d->syncSceneGraph(); if (lastDirtyWindow) m_rc->endSync(); + Q_TRACE(QSG_sync_exit); QSG_RENDER_TIMING_SAMPLE(QQuickProfiler::SceneGraphRenderLoopFrame, time_synced, QQuickProfiler::SceneGraphRenderLoopSync); + Q_TRACE(QSG_render_entry); RLDEBUG(" - rendering"); d->renderSceneGraph(window->size()); + Q_TRACE(QSG_render_exit); QSG_RENDER_TIMING_SAMPLE(QQuickProfiler::SceneGraphRenderLoopFrame, time_rendered, QQuickProfiler::SceneGraphRenderLoopRender); + Q_TRACE(QSG_swap_entry); RLDEBUG(" - swapping"); if (!d->customRenderStage || !d->customRenderStage->swap()) m_gl->swapBuffers(window); + Q_TRACE(QSG_swap_exit); QSG_RENDER_TIMING_SAMPLE(QQuickProfiler::SceneGraphRenderLoopFrame, time_swapped, QQuickProfiler::SceneGraphRenderLoopSwap); diff --git a/src/quick/scenegraph/util/qsgopenglatlastexture.cpp b/src/quick/scenegraph/util/qsgopenglatlastexture.cpp index 75a874424a..18c72286d1 100644 --- a/src/quick/scenegraph/util/qsgopenglatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgopenglatlastexture.cpp @@ -59,6 +59,8 @@ #include <private/qquickprofiler_p.h> +#include <qtquick_tracepoints_p.h> + QT_BEGIN_NAMESPACE #ifndef GL_BGRA @@ -250,14 +252,17 @@ void AtlasBase::bind(QSGTexture::Filtering filtering) if (profileFrames) qsg_renderer_timer.start(); + Q_TRACE_SCOPE(QSG_texture_prepare); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphTexturePrepare); // Skip bind, convert, swizzle; they're irrelevant Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare, QQuickProfiler::SceneGraphTexturePrepareStart, 3); + Q_TRACE(QSG_texture_upload_entry); uploadPendingTexture(i); + Q_TRACE(QSG_texture_upload_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare, QQuickProfiler::SceneGraphTexturePrepareUpload); diff --git a/src/quick/scenegraph/util/qsgplaintexture.cpp b/src/quick/scenegraph/util/qsgplaintexture.cpp index 0361a941db..cbc226fd6e 100644 --- a/src/quick/scenegraph/util/qsgplaintexture.cpp +++ b/src/quick/scenegraph/util/qsgplaintexture.cpp @@ -53,6 +53,8 @@ #endif #include <QtGui/private/qrhi_p.h> +#include <qtquick_tracepoints_p.h> + #if QT_CONFIG(opengl) static QElapsedTimer qsg_renderer_timer; #endif @@ -150,9 +152,11 @@ void QSGPlainTexture::setTextureId(int id) // legacy (GL-only) void QSGPlainTexture::bind() // legacy (GL-only) { #if QT_CONFIG(opengl) + Q_TRACE_SCOPE(QSG_texture_prepare); QOpenGLContext *context = QOpenGLContext::currentContext(); QOpenGLFunctions *funcs = context->functions(); if (!m_dirty_texture) { + Q_TRACE_SCOPE(QSG_texture_bind); funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id); if (mipmapFiltering() != QSGTexture::None && !m_mipmaps_generated) { funcs->glGenerateMipmap(GL_TEXTURE_2D); @@ -174,6 +178,7 @@ void QSGPlainTexture::bind() // legacy (GL-only) if (m_image.isNull()) { if (m_texture_id && m_owns_texture) { + Q_TRACE_SCOPE(QSG_texture_delete); funcs->glDeleteTextures(1, &m_texture_id); qCDebug(QSG_LOG_TIME_TEXTURE, "plain texture deleted in %dms - %dx%d", (int) qsg_renderer_timer.elapsed(), @@ -189,6 +194,8 @@ void QSGPlainTexture::bind() // legacy (GL-only) return; } + Q_TRACE(QSG_texture_bind_entry); + if (m_texture_id == 0) funcs->glGenTextures(1, &m_texture_id); funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id); @@ -196,8 +203,10 @@ void QSGPlainTexture::bind() // legacy (GL-only) qint64 bindTime = 0; if (profileFrames) bindTime = qsg_renderer_timer.nsecsElapsed(); + Q_TRACE(QSG_texture_bind_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare, QQuickProfiler::SceneGraphTexturePrepareBind); + Q_TRACE(QSG_texture_upload_entry); // ### TODO: check for out-of-memory situations... @@ -232,8 +241,10 @@ void QSGPlainTexture::bind() // legacy (GL-only) qint64 uploadTime = 0; if (profileFrames) uploadTime = qsg_renderer_timer.nsecsElapsed(); + Q_TRACE(QSG_texture_upload_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare, QQuickProfiler::SceneGraphTexturePrepareUpload); + Q_TRACE(QSG_texture_mipmap_entry); if (mipmapFiltering() != QSGTexture::None) { funcs->glGenerateMipmap(GL_TEXTURE_2D); @@ -252,6 +263,7 @@ void QSGPlainTexture::bind() // legacy (GL-only) int((mipmapTime - uploadTime)/1000000), m_texture_size != m_image.size() ? " (scaled to GL_MAX_TEXTURE_SIZE)" : ""); } + Q_TRACE(QSG_texture_mipmap_exit); Q_QUICK_SG_PROFILE_END(QQuickProfiler::SceneGraphTexturePrepare, QQuickProfiler::SceneGraphTexturePrepareMipmap); diff --git a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp index 3dc1f5f526..6b5f1aec4f 100644 --- a/src/quick/scenegraph/util/qsgrhiatlastexture.cpp +++ b/src/quick/scenegraph/util/qsgrhiatlastexture.cpp @@ -49,6 +49,9 @@ #include <private/qquickprofiler_p.h> #include <private/qsgdefaultrendercontext_p.h> #include <private/qsgtexture_p.h> + +#include <qtquick_tracepoints_p.h> + #if 0 #include <private/qsgcompressedtexture_p.h> #include <private/qsgcompressedatlastexture_p.h> @@ -197,14 +200,17 @@ void AtlasBase::updateRhiTexture(QRhiResourceUpdateBatch *resourceUpdates) if (profileFrames) qsg_renderer_timer.start(); + Q_TRACE_SCOPE(QSG_texture_prepare); Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphTexturePrepare); // Skip bind, convert, swizzle; they're irrelevant Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare, QQuickProfiler::SceneGraphTexturePrepareStart, 3); + Q_TRACE(QSG_texture_upload_entry); enqueueTextureUpload(t, resourceUpdates); + Q_TRACE(QSG_texture_upload_exit); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare, QQuickProfiler::SceneGraphTexturePrepareUpload); diff --git a/src/quick/util/qquickstyledtext.cpp b/src/quick/util/qquickstyledtext.cpp index 7d545cdb2f..08d06c66ab 100644 --- a/src/quick/util/qquickstyledtext.cpp +++ b/src/quick/util/qquickstyledtext.cpp @@ -89,8 +89,8 @@ public: QQmlContext *context, bool preloadImages, bool *fontSizeModified) - : text(t), layout(l), imgTags(&imgTags), baseFont(layout.font()), baseUrl(baseUrl), hasNewLine(true), nbImages(0), updateImagePositions(false) - , preFormat(false), prependSpace(false), hasSpace(true), preloadImages(preloadImages), fontSizeModified(fontSizeModified), context(context) + : text(t), layout(l), imgTags(&imgTags), baseFont(layout.font()), baseUrl(baseUrl), + fontSizeModified(fontSizeModified), context(context), preloadImages(preloadImages) { } @@ -122,15 +122,15 @@ public: QFont baseFont; QStack<List> listStack; QUrl baseUrl; - bool hasNewLine; - int nbImages; - bool updateImagePositions; - bool preFormat; - bool prependSpace; - bool hasSpace; - bool preloadImages; bool *fontSizeModified; QQmlContext *context; + int nbImages = 0; + bool hasNewLine = true; + bool updateImagePositions = false; + bool preFormat = false; + bool prependSpace = false; + bool hasSpace = true; + bool preloadImages; static const QChar lessThan; static const QChar greaterThan; diff --git a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp index 122e14a633..bb4c9a7c1e 100644 --- a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp +++ b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp @@ -168,6 +168,7 @@ void tst_QQuickWorkerScript::messaging_data() QRegExp::RegExp2)); QTest::newRow("regularexpression") << QVariant::fromValue(QRegularExpression( "^\\d\\d?$", QRegularExpression::CaseInsensitiveOption)); + QTest::newRow("url") << QVariant::fromValue(QUrl("http://example.com/foo/bar")); } void tst_QQuickWorkerScript::messaging_sendQObjectList() diff --git a/tools/qmllint/scopetree.h b/tools/qmllint/scopetree.h index 4b0d4a9539..d6f0a2c171 100644 --- a/tools/qmllint/scopetree.h +++ b/tools/qmllint/scopetree.h @@ -132,7 +132,11 @@ public: ScopeType scopeType() const { return m_scopeType; } - void addMethods(const QHash<QString, MetaMethod> &methods) { m_methods.unite(methods); } + void addMethods(const QHash<QString, MetaMethod> &methods) + { + for (auto it = methods.begin(), end = methods.end(); it != end; ++it) + m_methods.insert(it.key(), it.value()); + } void addMethod(const MetaMethod &method) { m_methods.insert(method.methodName(), method); } QHash<QString, MetaMethod> methods() const { return m_methods; } |