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