blob: a4201c1dfa2454f32841ad17e7c052c74b741a9c [file] [log] [blame]
/****************************************************************************
**
** 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"