| /**************************************************************************** |
| ** |
| ** 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" |