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