aboutsummaryrefslogtreecommitdiffstats
path: root/examples/quick
diff options
context:
space:
mode:
authorPaul Olav Tvete <paul.tvete@qt.io>2019-12-09 15:46:49 +0100
committerPaul Olav Tvete <paul.tvete@qt.io>2019-12-12 13:51:07 +0100
commitf2a727eb8454b635227bcee4966299e25de0ed5a (patch)
tree529cebb3605e6441302d3a0161278f6196df34a9 /examples/quick
parentfeb519d1b3705f3ee68047fe09396758e40ff549 (diff)
Add vulkantextureimport example
This is essentially the rendering code from vulkanunderqml put into the structure of metaltextureimport with some added memory barriers. Task-number: QTBUG-80500 Change-Id: If38301c039b38e3798ff482625daa5f63e8d6fb2 Reviewed-by: Laszlo Agocs <laszlo.agocs@qt.io>
Diffstat (limited to 'examples/quick')
-rw-r--r--examples/quick/scenegraph/scenegraph.pro4
-rw-r--r--examples/quick/scenegraph/vulkantextureimport/main.cpp70
-rw-r--r--examples/quick/scenegraph/vulkantextureimport/main.qml123
-rw-r--r--examples/quick/scenegraph/vulkantextureimport/squircle.frag.spvbin0 -> 1432 bytes
-rw-r--r--examples/quick/scenegraph/vulkantextureimport/squircle.vert.spvbin0 -> 644 bytes
-rw-r--r--examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.cpp823
-rw-r--r--examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.h88
-rw-r--r--examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.pro12
-rw-r--r--examples/quick/scenegraph/vulkantextureimport/vulkantextureimport.qrc7
9 files changed, 1126 insertions, 1 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/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
new file mode 100644
index 0000000000..e4d13a871d
--- /dev/null
+++ b/examples/quick/scenegraph/vulkantextureimport/squircle.frag.spv
Binary files differ
diff --git a/examples/quick/scenegraph/vulkantextureimport/squircle.vert.spv b/examples/quick/scenegraph/vulkantextureimport/squircle.vert.spv
new file mode 100644
index 0000000000..5df94a47e4
--- /dev/null
+++ b/examples/quick/scenegraph/vulkantextureimport/squircle.vert.spv
Binary files differ
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>