diff options
Diffstat (limited to 'examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp')
-rw-r--r-- | examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp | 623 |
1 files changed, 623 insertions, 0 deletions
diff --git a/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp new file mode 100644 index 0000000000..21f46a25c1 --- /dev/null +++ b/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp @@ -0,0 +1,623 @@ +/**************************************************************************** +** +** 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 "vulkansquircle.h" +#include <QtCore/QRunnable> +#include <QtQuick/QQuickWindow> + +#include <QVulkanInstance> +#include <QVulkanFunctions> + +class SquircleRenderer : public QObject +{ + Q_OBJECT +public: + ~SquircleRenderer(); + + void setT(qreal t) { m_t = t; } + void setViewportSize(const QSize &size) { m_viewportSize = size; } + void setWindow(QQuickWindow *window) { m_window = window; } + +public slots: + void frameStart(); + void mainPassRecordingStart(); + +private: + enum Stage { + VertexStage, + FragmentStage + }; + void prepareShader(Stage stage); + void init(int framesInFlight); + + QSize m_viewportSize; + qreal m_t = 0; + QQuickWindow *m_window; + + QByteArray m_vert; + QByteArray m_frag; + + bool m_initialized = false; + 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; +}; + +VulkanSquircle::VulkanSquircle() +{ + connect(this, &QQuickItem::windowChanged, this, &VulkanSquircle::handleWindowChanged); +} + +void VulkanSquircle::setT(qreal t) +{ + if (t == m_t) + return; + m_t = t; + emit tChanged(); + if (window()) + window()->update(); +} + +void VulkanSquircle::handleWindowChanged(QQuickWindow *win) +{ + if (win) { + connect(win, &QQuickWindow::beforeSynchronizing, this, &VulkanSquircle::sync, Qt::DirectConnection); + connect(win, &QQuickWindow::sceneGraphInvalidated, this, &VulkanSquircle::cleanup, Qt::DirectConnection); + + // Ensure we start with cleared to black. The squircle's blend mode relies on this. + win->setColor(Qt::black); + } +} + +// The safe way to release custom graphics resources is to both connect to +// sceneGraphInvalidated() and implement releaseResources(). To support +// threaded render loops the latter performs the SquircleRenderer destruction +// via scheduleRenderJob(). Note that the VulkanSquircle may be gone by the time +// the QRunnable is invoked. + +void VulkanSquircle::cleanup() +{ + delete m_renderer; + m_renderer = nullptr; +} + +class CleanupJob : public QRunnable +{ +public: + CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { } + void run() override { delete m_renderer; } +private: + SquircleRenderer *m_renderer; +}; + +void VulkanSquircle::releaseResources() +{ + window()->scheduleRenderJob(new CleanupJob(m_renderer), QQuickWindow::BeforeSynchronizingStage); + m_renderer = nullptr; +} + +SquircleRenderer::~SquircleRenderer() +{ + qDebug("cleanup"); + if (!m_devFuncs) + return; + + m_devFuncs->vkDestroyPipeline(m_dev, m_pipeline, nullptr); + m_devFuncs->vkDestroyPipelineLayout(m_dev, m_pipelineLayout, nullptr); + m_devFuncs->vkDestroyDescriptorSetLayout(m_dev, m_resLayout, nullptr); + + m_devFuncs->vkDestroyDescriptorPool(m_dev, m_descriptorPool, nullptr); + + m_devFuncs->vkDestroyPipelineCache(m_dev, m_pipelineCache, nullptr); + + m_devFuncs->vkDestroyBuffer(m_dev, m_vbuf, nullptr); + m_devFuncs->vkFreeMemory(m_dev, m_vbufMem, nullptr); + + m_devFuncs->vkDestroyBuffer(m_dev, m_ubuf, nullptr); + m_devFuncs->vkFreeMemory(m_dev, m_ubufMem, nullptr); + + qDebug("released"); +} + +void VulkanSquircle::sync() +{ + if (!m_renderer) { + m_renderer = new SquircleRenderer; + // Initializing resources is done before starting to record the + // renderpass, regardless of wanting an underlay or overlay. + connect(window(), &QQuickWindow::beforeRendering, m_renderer, &SquircleRenderer::frameStart, Qt::DirectConnection); + // Here we want an underlay and therefore connect to + // beforeRenderPassRecording. Changing to afterRenderPassRecording + // would render the squircle on top (overlay). + connect(window(), &QQuickWindow::beforeRenderPassRecording, m_renderer, &SquircleRenderer::mainPassRecordingStart, Qt::DirectConnection); + } + m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio()); + m_renderer->setT(m_t); + m_renderer->setWindow(window()); +} + +void SquircleRenderer::frameStart() +{ + QSGRendererInterface *rif = m_window->rendererInterface(); + + // We are not prepared for anything other than running with the RHI and its Vulkan backend. + Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::VulkanRhi); + + if (m_vert.isEmpty()) + prepareShader(VertexStage); + if (m_frag.isEmpty()) + prepareShader(FragmentStage); + + if (!m_initialized) + init(m_window->graphicsStateInfo().framesInFlight); +} + +static const float vertices[] = { + -1, -1, + 1, -1, + -1, 1, + 1, 1 +}; + +const int UBUF_SIZE = 4; + +void SquircleRenderer::mainPassRecordingStart() +{ + // This example demonstrates the simple case: prepending some commands to + // the scenegraph's main renderpass. It does not create its own passes, + // rendertargets, etc. so no synchronization is needed. + + const QQuickWindow::GraphicsStateInfo &stateInfo(m_window->graphicsStateInfo()); + QSGRendererInterface *rif = m_window->rendererInterface(); + + VkDeviceSize ubufOffset = stateInfo.currentFrameSlot * m_allocPerUbuf; + void *p = nullptr; + VkResult 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); + + m_window->beginExternalCommands(); + + // Must query the command buffer _after_ beginExternalCommands(), this is + // actually important when running on Vulkan because what we get here is a + // new secondary command buffer, not the primary one. + VkCommandBuffer cb = *reinterpret_cast<VkCommandBuffer *>( + rif->getResource(m_window, QSGRendererInterface::CommandListResource)); + Q_ASSERT(cb); + + // Do not assume any state persists on the command buffer. (it may be a + // brand new one that just started recording) + + m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline); + + VkDeviceSize vbufOffset = 0; + m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_vbuf, &vbufOffset); + + uint32_t dynamicOffset = m_allocPerUbuf * stateInfo.currentFrameSlot; + m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1, + &m_ubufDescriptor, 1, &dynamicOffset); + + VkViewport vp = { 0, 0, float(m_viewportSize.width()), float(m_viewportSize.height()), 0.0f, 1.0f }; + m_devFuncs->vkCmdSetViewport(cb, 0, 1, &vp); + VkRect2D scissor = { { 0, 0 }, { uint32_t(m_viewportSize.width()), uint32_t(m_viewportSize.height()) } }; + m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor); + + m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0); + + m_window->endExternalCommands(); +} + +void SquircleRenderer::prepareShader(Stage stage) +{ + QString filename; + if (stage == VertexStage) { + filename = QLatin1String(":/scenegraph/vulkanunderqml/squircle.vert.spv"); + } else { + Q_ASSERT(stage == FragmentStage); + filename = QLatin1String(":/scenegraph/vulkanunderqml/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()); + } +} + +static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign) +{ + return (v + byteAlign - 1) & ~(byteAlign - 1); +} + +void SquircleRenderer::init(int framesInFlight) +{ + qDebug("init"); + + Q_ASSERT(framesInFlight <= 3); + 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 = *reinterpret_cast<VkPhysicalDevice *>(rif->getResource(m_window, QSGRendererInterface::PhysicalDeviceResource)); + m_dev = *reinterpret_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); + + VkRenderPass rp = *reinterpret_cast<VkRenderPass *>( + rif->getResource(m_window, QSGRendererInterface::RenderPassResource)); + Q_ASSERT(rp); + + // For simplicity we just use host visible buffers instead of device local + staging. + + 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); + + // Now have a uniform buffer with enough space for the buffer data for each + // (potentially) in-flight frame. (as we will write the contents every + // frame, and so would need to wait for command buffer completion if there + // was only one, and that would not be nice) + + // Could have three buffers and three descriptor sets, or one buffer and + // one descriptor set and dynamic offset. We chose the latter in this + // example. + + // We use one memory allocation for all uniform buffers, but then have to + // watch out for the buffer offset aligment requirement, which may be as + // large as 256 bytes. + + 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 = 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 = rp; + + 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); +} + +#include "vulkansquircle.moc" |