1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the demonstration applications of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include "vulkansquircle.h"
52#include <QtCore/QRunnable>
53#include <QtQuick/QQuickWindow>
54
55#include <QVulkanInstance>
56#include <QVulkanFunctions>
57
58class SquircleRenderer : public QObject
59{
60 Q_OBJECT
61public:
62 ~SquircleRenderer();
63
64 void setT(qreal t) { m_t = t; }
65 void setViewportSize(const QSize &size) { m_viewportSize = size; }
66 void setWindow(QQuickWindow *window) { m_window = window; }
67
68public slots:
69 void frameStart();
70 void mainPassRecordingStart();
71
72private:
73 enum Stage {
74 VertexStage,
75 FragmentStage
76 };
77 void prepareShader(Stage stage);
78 void init(int framesInFlight);
79
80 QSize m_viewportSize;
81 qreal m_t = 0;
82 QQuickWindow *m_window;
83
84 QByteArray m_vert;
85 QByteArray m_frag;
86
87 bool m_initialized = false;
88 VkPhysicalDevice m_physDev = VK_NULL_HANDLE;
89 VkDevice m_dev = VK_NULL_HANDLE;
90 QVulkanDeviceFunctions *m_devFuncs = nullptr;
91 QVulkanFunctions *m_funcs = nullptr;
92
93 VkBuffer m_vbuf = VK_NULL_HANDLE;
94 VkDeviceMemory m_vbufMem = VK_NULL_HANDLE;
95 VkBuffer m_ubuf = VK_NULL_HANDLE;
96 VkDeviceMemory m_ubufMem = VK_NULL_HANDLE;
97 VkDeviceSize m_allocPerUbuf = 0;
98
99 VkPipelineCache m_pipelineCache = VK_NULL_HANDLE;
100
101 VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
102 VkDescriptorSetLayout m_resLayout = VK_NULL_HANDLE;
103 VkPipeline m_pipeline = VK_NULL_HANDLE;
104
105 VkDescriptorPool m_descriptorPool = VK_NULL_HANDLE;
106 VkDescriptorSet m_ubufDescriptor = VK_NULL_HANDLE;
107};
108
109VulkanSquircle::VulkanSquircle()
110{
111 connect(sender: this, signal: &QQuickItem::windowChanged, receiver: this, slot: &VulkanSquircle::handleWindowChanged);
112}
113
114void VulkanSquircle::setT(qreal t)
115{
116 if (t == m_t)
117 return;
118 m_t = t;
119 emit tChanged();
120 if (window())
121 window()->update();
122}
123
124void VulkanSquircle::handleWindowChanged(QQuickWindow *win)
125{
126 if (win) {
127 connect(sender: win, signal: &QQuickWindow::beforeSynchronizing, receiver: this, slot: &VulkanSquircle::sync, type: Qt::DirectConnection);
128 connect(sender: win, signal: &QQuickWindow::sceneGraphInvalidated, receiver: this, slot: &VulkanSquircle::cleanup, type: Qt::DirectConnection);
129
130 // Ensure we start with cleared to black. The squircle's blend mode relies on this.
131 win->setColor(Qt::black);
132 }
133}
134
135// The safe way to release custom graphics resources is to both connect to
136// sceneGraphInvalidated() and implement releaseResources(). To support
137// threaded render loops the latter performs the SquircleRenderer destruction
138// via scheduleRenderJob(). Note that the VulkanSquircle may be gone by the time
139// the QRunnable is invoked.
140
141void VulkanSquircle::cleanup()
142{
143 delete m_renderer;
144 m_renderer = nullptr;
145}
146
147class CleanupJob : public QRunnable
148{
149public:
150 CleanupJob(SquircleRenderer *renderer) : m_renderer(renderer) { }
151 void run() override { delete m_renderer; }
152private:
153 SquircleRenderer *m_renderer;
154};
155
156void VulkanSquircle::releaseResources()
157{
158 window()->scheduleRenderJob(job: new CleanupJob(m_renderer), schedule: QQuickWindow::BeforeSynchronizingStage);
159 m_renderer = nullptr;
160}
161
162SquircleRenderer::~SquircleRenderer()
163{
164 qDebug(msg: "cleanup");
165 if (!m_devFuncs)
166 return;
167
168 m_devFuncs->vkDestroyPipeline(m_dev, m_pipeline, nullptr);
169 m_devFuncs->vkDestroyPipelineLayout(m_dev, m_pipelineLayout, nullptr);
170 m_devFuncs->vkDestroyDescriptorSetLayout(m_dev, m_resLayout, nullptr);
171
172 m_devFuncs->vkDestroyDescriptorPool(m_dev, m_descriptorPool, nullptr);
173
174 m_devFuncs->vkDestroyPipelineCache(m_dev, m_pipelineCache, nullptr);
175
176 m_devFuncs->vkDestroyBuffer(m_dev, m_vbuf, nullptr);
177 m_devFuncs->vkFreeMemory(m_dev, m_vbufMem, nullptr);
178
179 m_devFuncs->vkDestroyBuffer(m_dev, m_ubuf, nullptr);
180 m_devFuncs->vkFreeMemory(m_dev, m_ubufMem, nullptr);
181
182 qDebug(msg: "released");
183}
184
185void VulkanSquircle::sync()
186{
187 if (!m_renderer) {
188 m_renderer = new SquircleRenderer;
189 // Initializing resources is done before starting to record the
190 // renderpass, regardless of wanting an underlay or overlay.
191 connect(sender: window(), signal: &QQuickWindow::beforeRendering, receiver: m_renderer, slot: &SquircleRenderer::frameStart, type: Qt::DirectConnection);
192 // Here we want an underlay and therefore connect to
193 // beforeRenderPassRecording. Changing to afterRenderPassRecording
194 // would render the squircle on top (overlay).
195 connect(sender: window(), signal: &QQuickWindow::beforeRenderPassRecording, receiver: m_renderer, slot: &SquircleRenderer::mainPassRecordingStart, type: Qt::DirectConnection);
196 }
197 m_renderer->setViewportSize(window()->size() * window()->devicePixelRatio());
198 m_renderer->setT(m_t);
199 m_renderer->setWindow(window());
200}
201
202void SquircleRenderer::frameStart()
203{
204 QSGRendererInterface *rif = m_window->rendererInterface();
205
206 // We are not prepared for anything other than running with the RHI and its Vulkan backend.
207 Q_ASSERT(rif->graphicsApi() == QSGRendererInterface::VulkanRhi);
208
209 if (m_vert.isEmpty())
210 prepareShader(stage: VertexStage);
211 if (m_frag.isEmpty())
212 prepareShader(stage: FragmentStage);
213
214 if (!m_initialized)
215 init(framesInFlight: m_window->graphicsStateInfo().framesInFlight);
216}
217
218static const float vertices[] = {
219 -1, -1,
220 1, -1,
221 -1, 1,
222 1, 1
223};
224
225const int UBUF_SIZE = 4;
226
227void SquircleRenderer::mainPassRecordingStart()
228{
229 // This example demonstrates the simple case: prepending some commands to
230 // the scenegraph's main renderpass. It does not create its own passes,
231 // rendertargets, etc. so no synchronization is needed.
232
233 const QQuickWindow::GraphicsStateInfo &stateInfo(m_window->graphicsStateInfo());
234 QSGRendererInterface *rif = m_window->rendererInterface();
235
236 VkDeviceSize ubufOffset = stateInfo.currentFrameSlot * m_allocPerUbuf;
237 void *p = nullptr;
238 VkResult err = m_devFuncs->vkMapMemory(m_dev, m_ubufMem, ubufOffset, m_allocPerUbuf, 0, &p);
239 if (err != VK_SUCCESS || !p)
240 qFatal(msg: "Failed to map uniform buffer memory: %d", err);
241 float t = m_t;
242 memcpy(dest: p, src: &t, n: 4);
243 m_devFuncs->vkUnmapMemory(m_dev, m_ubufMem);
244
245 m_window->beginExternalCommands();
246
247 // Must query the command buffer _after_ beginExternalCommands(), this is
248 // actually important when running on Vulkan because what we get here is a
249 // new secondary command buffer, not the primary one.
250 VkCommandBuffer cb = *reinterpret_cast<VkCommandBuffer *>(
251 rif->getResource(window: m_window, resource: QSGRendererInterface::CommandListResource));
252 Q_ASSERT(cb);
253
254 // Do not assume any state persists on the command buffer. (it may be a
255 // brand new one that just started recording)
256
257 m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
258
259 VkDeviceSize vbufOffset = 0;
260 m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_vbuf, &vbufOffset);
261
262 uint32_t dynamicOffset = m_allocPerUbuf * stateInfo.currentFrameSlot;
263 m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1,
264 &m_ubufDescriptor, 1, &dynamicOffset);
265
266 VkViewport vp = { .x: 0, .y: 0, .width: float(m_viewportSize.width()), .height: float(m_viewportSize.height()), .minDepth: 0.0f, .maxDepth: 1.0f };
267 m_devFuncs->vkCmdSetViewport(cb, 0, 1, &vp);
268 VkRect2D scissor = { .offset: { .x: 0, .y: 0 }, .extent: { .width: uint32_t(m_viewportSize.width()), .height: uint32_t(m_viewportSize.height()) } };
269 m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor);
270
271 m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0);
272
273 m_window->endExternalCommands();
274}
275
276void SquircleRenderer::prepareShader(Stage stage)
277{
278 QString filename;
279 if (stage == VertexStage) {
280 filename = QLatin1String(":/scenegraph/vulkanunderqml/squircle.vert.spv");
281 } else {
282 Q_ASSERT(stage == FragmentStage);
283 filename = QLatin1String(":/scenegraph/vulkanunderqml/squircle.frag.spv");
284 }
285 QFile f(filename);
286 if (!f.open(flags: QIODevice::ReadOnly))
287 qFatal(msg: "Failed to read shader %s", qPrintable(filename));
288
289 const QByteArray contents = f.readAll();
290
291 if (stage == VertexStage) {
292 m_vert = contents;
293 Q_ASSERT(!m_vert.isEmpty());
294 } else {
295 m_frag = contents;
296 Q_ASSERT(!m_frag.isEmpty());
297 }
298}
299
300static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
301{
302 return (v + byteAlign - 1) & ~(byteAlign - 1);
303}
304
305void SquircleRenderer::init(int framesInFlight)
306{
307 qDebug(msg: "init");
308
309 Q_ASSERT(framesInFlight <= 3);
310 m_initialized = true;
311
312 QSGRendererInterface *rif = m_window->rendererInterface();
313 QVulkanInstance *inst = reinterpret_cast<QVulkanInstance *>(
314 rif->getResource(window: m_window, resource: QSGRendererInterface::VulkanInstanceResource));
315 Q_ASSERT(inst && inst->isValid());
316
317 m_physDev = *reinterpret_cast<VkPhysicalDevice *>(rif->getResource(window: m_window, resource: QSGRendererInterface::PhysicalDeviceResource));
318 m_dev = *reinterpret_cast<VkDevice *>(rif->getResource(window: m_window, resource: QSGRendererInterface::DeviceResource));
319 Q_ASSERT(m_physDev && m_dev);
320
321 m_devFuncs = inst->deviceFunctions(device: m_dev);
322 m_funcs = inst->functions();
323 Q_ASSERT(m_devFuncs && m_funcs);
324
325 VkRenderPass rp = *reinterpret_cast<VkRenderPass *>(
326 rif->getResource(window: m_window, resource: QSGRendererInterface::RenderPassResource));
327 Q_ASSERT(rp);
328
329 // For simplicity we just use host visible buffers instead of device local + staging.
330
331 VkPhysicalDeviceProperties physDevProps;
332 m_funcs->vkGetPhysicalDeviceProperties(m_physDev, &physDevProps);
333
334 VkPhysicalDeviceMemoryProperties physDevMemProps;
335 m_funcs->vkGetPhysicalDeviceMemoryProperties(m_physDev, &physDevMemProps);
336
337 VkBufferCreateInfo bufferInfo;
338 memset(s: &bufferInfo, c: 0, n: sizeof(bufferInfo));
339 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
340 bufferInfo.size = sizeof(vertices);
341 bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
342 VkResult err = m_devFuncs->vkCreateBuffer(m_dev, &bufferInfo, nullptr, &m_vbuf);
343 if (err != VK_SUCCESS)
344 qFatal(msg: "Failed to create vertex buffer: %d", err);
345
346 VkMemoryRequirements memReq;
347 m_devFuncs->vkGetBufferMemoryRequirements(m_dev, m_vbuf, &memReq);
348 VkMemoryAllocateInfo allocInfo;
349 memset(s: &allocInfo, c: 0, n: sizeof(allocInfo));
350 allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
351 allocInfo.allocationSize = memReq.size;
352
353 uint32_t memTypeIndex = uint32_t(-1);
354 const VkMemoryType *memType = physDevMemProps.memoryTypes;
355 for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
356 if (memReq.memoryTypeBits & (1 << i)) {
357 if ((memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
358 && (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
359 {
360 memTypeIndex = i;
361 break;
362 }
363 }
364 }
365 if (memTypeIndex == uint32_t(-1))
366 qFatal(msg: "Failed to find host visible and coherent memory type");
367
368 allocInfo.memoryTypeIndex = memTypeIndex;
369 err = m_devFuncs->vkAllocateMemory(m_dev, &allocInfo, nullptr, &m_vbufMem);
370 if (err != VK_SUCCESS)
371 qFatal(msg: "Failed to allocate vertex buffer memory of size %u: %d", uint(allocInfo.allocationSize), err);
372
373 void *p = nullptr;
374 err = m_devFuncs->vkMapMemory(m_dev, m_vbufMem, 0, allocInfo.allocationSize, 0, &p);
375 if (err != VK_SUCCESS || !p)
376 qFatal(msg: "Failed to map vertex buffer memory: %d", err);
377 memcpy(dest: p, src: vertices, n: sizeof(vertices));
378 m_devFuncs->vkUnmapMemory(m_dev, m_vbufMem);
379 err = m_devFuncs->vkBindBufferMemory(m_dev, m_vbuf, m_vbufMem, 0);
380 if (err != VK_SUCCESS)
381 qFatal(msg: "Failed to bind vertex buffer memory: %d", err);
382
383 // Now have a uniform buffer with enough space for the buffer data for each
384 // (potentially) in-flight frame. (as we will write the contents every
385 // frame, and so would need to wait for command buffer completion if there
386 // was only one, and that would not be nice)
387
388 // Could have three buffers and three descriptor sets, or one buffer and
389 // one descriptor set and dynamic offset. We chose the latter in this
390 // example.
391
392 // We use one memory allocation for all uniform buffers, but then have to
393 // watch out for the buffer offset aligment requirement, which may be as
394 // large as 256 bytes.
395
396 m_allocPerUbuf = aligned(v: UBUF_SIZE, byteAlign: physDevProps.limits.minUniformBufferOffsetAlignment);
397
398 bufferInfo.size = framesInFlight * m_allocPerUbuf;
399 bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
400 err = m_devFuncs->vkCreateBuffer(m_dev, &bufferInfo, nullptr, &m_ubuf);
401 if (err != VK_SUCCESS)
402 qFatal(msg: "Failed to create uniform buffer: %d", err);
403 m_devFuncs->vkGetBufferMemoryRequirements(m_dev, m_ubuf, &memReq);
404 memTypeIndex = -1;
405 for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
406 if (memReq.memoryTypeBits & (1 << i)) {
407 if ((memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
408 && (memType[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))
409 {
410 memTypeIndex = i;
411 break;
412 }
413 }
414 }
415 if (memTypeIndex == uint32_t(-1))
416 qFatal(msg: "Failed to find host visible and coherent memory type");
417
418 allocInfo.allocationSize = framesInFlight * m_allocPerUbuf;
419 allocInfo.memoryTypeIndex = memTypeIndex;
420 err = m_devFuncs->vkAllocateMemory(m_dev, &allocInfo, nullptr, &m_ubufMem);
421 if (err != VK_SUCCESS)
422 qFatal(msg: "Failed to allocate uniform buffer memory of size %u: %d", uint(allocInfo.allocationSize), err);
423
424 err = m_devFuncs->vkBindBufferMemory(m_dev, m_ubuf, m_ubufMem, 0);
425 if (err != VK_SUCCESS)
426 qFatal(msg: "Failed to bind uniform buffer memory: %d", err);
427
428 // Now onto the pipeline.
429
430 VkPipelineCacheCreateInfo pipelineCacheInfo;
431 memset(s: &pipelineCacheInfo, c: 0, n: sizeof(pipelineCacheInfo));
432 pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
433 err = m_devFuncs->vkCreatePipelineCache(m_dev, &pipelineCacheInfo, nullptr, &m_pipelineCache);
434 if (err != VK_SUCCESS)
435 qFatal(msg: "Failed to create pipeline cache: %d", err);
436
437 VkDescriptorSetLayoutBinding descLayoutBinding;
438 memset(s: &descLayoutBinding, c: 0, n: sizeof(descLayoutBinding));
439 descLayoutBinding.binding = 0;
440 descLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
441 descLayoutBinding.descriptorCount = 1;
442 descLayoutBinding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
443 VkDescriptorSetLayoutCreateInfo layoutInfo;
444 memset(s: &layoutInfo, c: 0, n: sizeof(layoutInfo));
445 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
446 layoutInfo.bindingCount = 1;
447 layoutInfo.pBindings = &descLayoutBinding;
448 err = m_devFuncs->vkCreateDescriptorSetLayout(m_dev, &layoutInfo, nullptr, &m_resLayout);
449 if (err != VK_SUCCESS)
450 qFatal(msg: "Failed to create descriptor set layout: %d", err);
451
452 VkPipelineLayoutCreateInfo pipelineLayoutInfo;
453 memset(s: &pipelineLayoutInfo, c: 0, n: sizeof(pipelineLayoutInfo));
454 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
455 pipelineLayoutInfo.setLayoutCount = 1;
456 pipelineLayoutInfo.pSetLayouts = &m_resLayout;
457 err = m_devFuncs->vkCreatePipelineLayout(m_dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout);
458 if (err != VK_SUCCESS)
459 qWarning(msg: "Failed to create pipeline layout: %d", err);
460
461 VkGraphicsPipelineCreateInfo pipelineInfo;
462 memset(s: &pipelineInfo, c: 0, n: sizeof(pipelineInfo));
463 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
464
465 VkShaderModuleCreateInfo shaderInfo;
466 memset(s: &shaderInfo, c: 0, n: sizeof(shaderInfo));
467 shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
468 shaderInfo.codeSize = m_vert.size();
469 shaderInfo.pCode = reinterpret_cast<const quint32 *>(m_vert.constData());
470 VkShaderModule vertShaderModule;
471 err = m_devFuncs->vkCreateShaderModule(m_dev, &shaderInfo, nullptr, &vertShaderModule);
472 if (err != VK_SUCCESS)
473 qFatal(msg: "Failed to create vertex shader module: %d", err);
474
475 shaderInfo.codeSize = m_frag.size();
476 shaderInfo.pCode = reinterpret_cast<const quint32 *>(m_frag.constData());
477 VkShaderModule fragShaderModule;
478 err = m_devFuncs->vkCreateShaderModule(m_dev, &shaderInfo, nullptr, &fragShaderModule);
479 if (err != VK_SUCCESS)
480 qFatal(msg: "Failed to create fragment shader module: %d", err);
481
482 VkPipelineShaderStageCreateInfo stageInfo[2];
483 memset(s: &stageInfo, c: 0, n: sizeof(stageInfo));
484 stageInfo[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
485 stageInfo[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
486 stageInfo[0].module = vertShaderModule;
487 stageInfo[0].pName = "main";
488 stageInfo[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
489 stageInfo[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
490 stageInfo[1].module = fragShaderModule;
491 stageInfo[1].pName = "main";
492 pipelineInfo.stageCount = 2;
493 pipelineInfo.pStages = stageInfo;
494
495 VkVertexInputBindingDescription vertexBinding = {
496 .binding: 0, // binding
497 .stride: 2 * sizeof(float), // stride
498 .inputRate: VK_VERTEX_INPUT_RATE_VERTEX
499 };
500 VkVertexInputAttributeDescription vertexAttr = {
501 .location: 0, // location
502 .binding: 0, // binding
503 .format: VK_FORMAT_R32G32_SFLOAT, // 'vertices' only has 2 floats per vertex
504 .offset: 0 // offset
505 };
506 VkPipelineVertexInputStateCreateInfo vertexInputInfo;
507 memset(s: &vertexInputInfo, c: 0, n: sizeof(vertexInputInfo));
508 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
509 vertexInputInfo.vertexBindingDescriptionCount = 1;
510 vertexInputInfo.pVertexBindingDescriptions = &vertexBinding;
511 vertexInputInfo.vertexAttributeDescriptionCount = 1;
512 vertexInputInfo.pVertexAttributeDescriptions = &vertexAttr;
513 pipelineInfo.pVertexInputState = &vertexInputInfo;
514
515 VkDynamicState dynStates[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
516 VkPipelineDynamicStateCreateInfo dynamicInfo;
517 memset(s: &dynamicInfo, c: 0, n: sizeof(dynamicInfo));
518 dynamicInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
519 dynamicInfo.dynamicStateCount = 2;
520 dynamicInfo.pDynamicStates = dynStates;
521 pipelineInfo.pDynamicState = &dynamicInfo;
522
523 VkPipelineViewportStateCreateInfo viewportInfo;
524 memset(s: &viewportInfo, c: 0, n: sizeof(viewportInfo));
525 viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
526 viewportInfo.viewportCount = viewportInfo.scissorCount = 1;
527 pipelineInfo.pViewportState = &viewportInfo;
528
529 VkPipelineInputAssemblyStateCreateInfo iaInfo;
530 memset(s: &iaInfo, c: 0, n: sizeof(iaInfo));
531 iaInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
532 iaInfo.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
533 pipelineInfo.pInputAssemblyState = &iaInfo;
534
535 VkPipelineRasterizationStateCreateInfo rsInfo;
536 memset(s: &rsInfo, c: 0, n: sizeof(rsInfo));
537 rsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
538 rsInfo.lineWidth = 1.0f;
539 pipelineInfo.pRasterizationState = &rsInfo;
540
541 VkPipelineMultisampleStateCreateInfo msInfo;
542 memset(s: &msInfo, c: 0, n: sizeof(msInfo));
543 msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
544 msInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
545 pipelineInfo.pMultisampleState = &msInfo;
546
547 VkPipelineDepthStencilStateCreateInfo dsInfo;
548 memset(s: &dsInfo, c: 0, n: sizeof(dsInfo));
549 dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
550 pipelineInfo.pDepthStencilState = &dsInfo;
551
552 // SrcAlpha, One
553 VkPipelineColorBlendStateCreateInfo blendInfo;
554 memset(s: &blendInfo, c: 0, n: sizeof(blendInfo));
555 blendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
556 VkPipelineColorBlendAttachmentState blend;
557 memset(s: &blend, c: 0, n: sizeof(blend));
558 blend.blendEnable = true;
559 blend.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
560 blend.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
561 blend.colorBlendOp = VK_BLEND_OP_ADD;
562 blend.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
563 blend.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
564 blend.alphaBlendOp = VK_BLEND_OP_ADD;
565 blend.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT
566 | VK_COLOR_COMPONENT_A_BIT;
567 blendInfo.attachmentCount = 1;
568 blendInfo.pAttachments = &blend;
569 pipelineInfo.pColorBlendState = &blendInfo;
570
571 pipelineInfo.layout = m_pipelineLayout;
572
573 pipelineInfo.renderPass = rp;
574
575 err = m_devFuncs->vkCreateGraphicsPipelines(m_dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline);
576
577 m_devFuncs->vkDestroyShaderModule(m_dev, vertShaderModule, nullptr);
578 m_devFuncs->vkDestroyShaderModule(m_dev, fragShaderModule, nullptr);
579
580 if (err != VK_SUCCESS)
581 qFatal(msg: "Failed to create graphics pipeline: %d", err);
582
583 // Now just need some descriptors.
584 VkDescriptorPoolSize descPoolSizes[] = {
585 { .type: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, .descriptorCount: 1 }
586 };
587 VkDescriptorPoolCreateInfo descPoolInfo;
588 memset(s: &descPoolInfo, c: 0, n: sizeof(descPoolInfo));
589 descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
590 descPoolInfo.flags = 0; // won't use vkFreeDescriptorSets
591 descPoolInfo.maxSets = 1;
592 descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]);
593 descPoolInfo.pPoolSizes = descPoolSizes;
594 err = m_devFuncs->vkCreateDescriptorPool(m_dev, &descPoolInfo, nullptr, &m_descriptorPool);
595 if (err != VK_SUCCESS)
596 qFatal(msg: "Failed to create descriptor pool: %d", err);
597
598 VkDescriptorSetAllocateInfo descAllocInfo;
599 memset(s: &descAllocInfo, c: 0, n: sizeof(descAllocInfo));
600 descAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
601 descAllocInfo.descriptorPool = m_descriptorPool;
602 descAllocInfo.descriptorSetCount = 1;
603 descAllocInfo.pSetLayouts = &m_resLayout;
604 err = m_devFuncs->vkAllocateDescriptorSets(m_dev, &descAllocInfo, &m_ubufDescriptor);
605 if (err != VK_SUCCESS)
606 qFatal(msg: "Failed to allocate descriptor set");
607
608 VkWriteDescriptorSet writeInfo;
609 memset(s: &writeInfo, c: 0, n: sizeof(writeInfo));
610 writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
611 writeInfo.dstSet = m_ubufDescriptor;
612 writeInfo.dstBinding = 0;
613 writeInfo.descriptorCount = 1;
614 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
615 VkDescriptorBufferInfo bufInfo;
616 bufInfo.buffer = m_ubuf;
617 bufInfo.offset = 0; // dynamic offset is used so this is ignored
618 bufInfo.range = UBUF_SIZE;
619 writeInfo.pBufferInfo = &bufInfo;
620 m_devFuncs->vkUpdateDescriptorSets(m_dev, 1, &writeInfo, 0, nullptr);
621}
622
623#include "vulkansquircle.moc"
624

source code of qtdeclarative/examples/quick/scenegraph/vulkanunderqml/vulkansquircle.cpp