/**************************************************************************** ** ** 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 #include #include #include #include #include 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(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( rif->getResource(m_window, QSGRendererInterface::VulkanInstanceResource)); Q_ASSERT(inst && inst->isValid()); m_physDev = *static_cast(rif->getResource(m_window, QSGRendererInterface::PhysicalDeviceResource)); m_dev = *static_cast(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(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(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(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( 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"