1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples 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 "hellovulkantexture.h"
52#include <QVulkanFunctions>
53#include <QCoreApplication>
54#include <QFile>
55
56// Use a triangle strip to get a quad.
57//
58// Note that the vertex data and the projection matrix assume OpenGL. With
59// Vulkan Y is negated in clip space and the near/far plane is at 0/1 instead
60// of -1/1. These will be corrected for by an extra transformation when
61// calculating the modelview-projection matrix.
62static float vertexData[] = { // Y up, front = CW
63 // x, y, z, u, v
64 -1, -1, 0, 0, 1,
65 -1, 1, 0, 0, 0,
66 1, -1, 0, 1, 1,
67 1, 1, 0, 1, 0
68};
69
70static const int UNIFORM_DATA_SIZE = 16 * sizeof(float);
71
72static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
73{
74 return (v + byteAlign - 1) & ~(byteAlign - 1);
75}
76
77QVulkanWindowRenderer *VulkanWindow::createRenderer()
78{
79 return new VulkanRenderer(this);
80}
81
82VulkanRenderer::VulkanRenderer(QVulkanWindow *w)
83 : m_window(w)
84{
85}
86
87VkShaderModule VulkanRenderer::createShader(const QString &name)
88{
89 QFile file(name);
90 if (!file.open(flags: QIODevice::ReadOnly)) {
91 qWarning(msg: "Failed to read shader %s", qPrintable(name));
92 return VK_NULL_HANDLE;
93 }
94 QByteArray blob = file.readAll();
95 file.close();
96
97 VkShaderModuleCreateInfo shaderInfo;
98 memset(s: &shaderInfo, c: 0, n: sizeof(shaderInfo));
99 shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
100 shaderInfo.codeSize = blob.size();
101 shaderInfo.pCode = reinterpret_cast<const uint32_t *>(blob.constData());
102 VkShaderModule shaderModule;
103 VkResult err = m_devFuncs->vkCreateShaderModule(m_window->device(), &shaderInfo, nullptr, &shaderModule);
104 if (err != VK_SUCCESS) {
105 qWarning(msg: "Failed to create shader module: %d", err);
106 return VK_NULL_HANDLE;
107 }
108
109 return shaderModule;
110}
111
112bool VulkanRenderer::createTexture(const QString &name)
113{
114 QImage img(name);
115 if (img.isNull()) {
116 qWarning(msg: "Failed to load image %s", qPrintable(name));
117 return false;
118 }
119
120 // Convert to byte ordered RGBA8. Use premultiplied alpha, see pColorBlendState in the pipeline.
121 img = img.convertToFormat(f: QImage::Format_RGBA8888_Premultiplied);
122
123 QVulkanFunctions *f = m_window->vulkanInstance()->functions();
124 VkDevice dev = m_window->device();
125
126 const bool srgb = QCoreApplication::arguments().contains(QStringLiteral("--srgb"));
127 if (srgb)
128 qDebug(msg: "sRGB swapchain was requested, making texture sRGB too");
129
130 m_texFormat = srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
131
132 // Now we can either map and copy the image data directly, or have to go
133 // through a staging buffer to copy and convert into the internal optimal
134 // tiling format.
135 VkFormatProperties props;
136 f->vkGetPhysicalDeviceFormatProperties(m_window->physicalDevice(), m_texFormat, &props);
137 const bool canSampleLinear = (props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
138 const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
139 if (!canSampleLinear && !canSampleOptimal) {
140 qWarning(msg: "Neither linear nor optimal image sampling is supported for RGBA8");
141 return false;
142 }
143
144 static bool alwaysStage = qEnvironmentVariableIntValue(varName: "QT_VK_FORCE_STAGE_TEX");
145
146 if (canSampleLinear && !alwaysStage) {
147 if (!createTextureImage(size: img.size(), image: &m_texImage, mem: &m_texMem,
148 tiling: VK_IMAGE_TILING_LINEAR, usage: VK_IMAGE_USAGE_SAMPLED_BIT,
149 memIndex: m_window->hostVisibleMemoryIndex()))
150 return false;
151
152 if (!writeLinearImage(img, image: m_texImage, memory: m_texMem))
153 return false;
154
155 m_texLayoutPending = true;
156 } else {
157 if (!createTextureImage(size: img.size(), image: &m_texStaging, mem: &m_texStagingMem,
158 tiling: VK_IMAGE_TILING_LINEAR, usage: VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
159 memIndex: m_window->hostVisibleMemoryIndex()))
160 return false;
161
162 if (!createTextureImage(size: img.size(), image: &m_texImage, mem: &m_texMem,
163 tiling: VK_IMAGE_TILING_OPTIMAL, usage: VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
164 memIndex: m_window->deviceLocalMemoryIndex()))
165 return false;
166
167 if (!writeLinearImage(img, image: m_texStaging, memory: m_texStagingMem))
168 return false;
169
170 m_texStagingPending = true;
171 }
172
173 VkImageViewCreateInfo viewInfo;
174 memset(s: &viewInfo, c: 0, n: sizeof(viewInfo));
175 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
176 viewInfo.image = m_texImage;
177 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
178 viewInfo.format = m_texFormat;
179 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
180 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
181 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
182 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
183 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
184 viewInfo.subresourceRange.levelCount = viewInfo.subresourceRange.layerCount = 1;
185
186 VkResult err = m_devFuncs->vkCreateImageView(dev, &viewInfo, nullptr, &m_texView);
187 if (err != VK_SUCCESS) {
188 qWarning(msg: "Failed to create image view for texture: %d", err);
189 return false;
190 }
191
192 m_texSize = img.size();
193
194 return true;
195}
196
197bool VulkanRenderer::createTextureImage(const QSize &size, VkImage *image, VkDeviceMemory *mem,
198 VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex)
199{
200 VkDevice dev = m_window->device();
201
202 VkImageCreateInfo imageInfo;
203 memset(s: &imageInfo, c: 0, n: sizeof(imageInfo));
204 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
205 imageInfo.imageType = VK_IMAGE_TYPE_2D;
206 imageInfo.format = m_texFormat;
207 imageInfo.extent.width = size.width();
208 imageInfo.extent.height = size.height();
209 imageInfo.extent.depth = 1;
210 imageInfo.mipLevels = 1;
211 imageInfo.arrayLayers = 1;
212 imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
213 imageInfo.tiling = tiling;
214 imageInfo.usage = usage;
215 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
216
217 VkResult err = m_devFuncs->vkCreateImage(dev, &imageInfo, nullptr, image);
218 if (err != VK_SUCCESS) {
219 qWarning(msg: "Failed to create linear image for texture: %d", err);
220 return false;
221 }
222
223 VkMemoryRequirements memReq;
224 m_devFuncs->vkGetImageMemoryRequirements(dev, *image, &memReq);
225
226 if (!(memReq.memoryTypeBits & (1 << memIndex))) {
227 VkPhysicalDeviceMemoryProperties physDevMemProps;
228 m_window->vulkanInstance()->functions()->vkGetPhysicalDeviceMemoryProperties(m_window->physicalDevice(), &physDevMemProps);
229 for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i) {
230 if (!(memReq.memoryTypeBits & (1 << i)))
231 continue;
232 memIndex = i;
233 }
234 }
235
236 VkMemoryAllocateInfo allocInfo = {
237 .sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
238 .pNext: nullptr,
239 .allocationSize: memReq.size,
240 .memoryTypeIndex: memIndex
241 };
242 qDebug(msg: "allocating %u bytes for texture image", uint32_t(memReq.size));
243
244 err = m_devFuncs->vkAllocateMemory(dev, &allocInfo, nullptr, mem);
245 if (err != VK_SUCCESS) {
246 qWarning(msg: "Failed to allocate memory for linear image: %d", err);
247 return false;
248 }
249
250 err = m_devFuncs->vkBindImageMemory(dev, *image, *mem, 0);
251 if (err != VK_SUCCESS) {
252 qWarning(msg: "Failed to bind linear image memory: %d", err);
253 return false;
254 }
255
256 return true;
257}
258
259bool VulkanRenderer::writeLinearImage(const QImage &img, VkImage image, VkDeviceMemory memory)
260{
261 VkDevice dev = m_window->device();
262
263 VkImageSubresource subres = {
264 .aspectMask: VK_IMAGE_ASPECT_COLOR_BIT,
265 .mipLevel: 0, // mip level
266 .arrayLayer: 0
267 };
268 VkSubresourceLayout layout;
269 m_devFuncs->vkGetImageSubresourceLayout(dev, image, &subres, &layout);
270
271 uchar *p;
272 VkResult err = m_devFuncs->vkMapMemory(dev, memory, layout.offset, layout.size, 0, reinterpret_cast<void **>(&p));
273 if (err != VK_SUCCESS) {
274 qWarning(msg: "Failed to map memory for linear image: %d", err);
275 return false;
276 }
277
278 for (int y = 0; y < img.height(); ++y) {
279 const uchar *line = img.constScanLine(y);
280 memcpy(dest: p, src: line, n: img.width() * 4);
281 p += layout.rowPitch;
282 }
283
284 m_devFuncs->vkUnmapMemory(dev, memory);
285 return true;
286}
287
288void VulkanRenderer::ensureTexture()
289{
290 if (!m_texLayoutPending && !m_texStagingPending)
291 return;
292
293 Q_ASSERT(m_texLayoutPending != m_texStagingPending);
294 VkCommandBuffer cb = m_window->currentCommandBuffer();
295
296 VkImageMemoryBarrier barrier;
297 memset(s: &barrier, c: 0, n: sizeof(barrier));
298 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
299 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
300 barrier.subresourceRange.levelCount = barrier.subresourceRange.layerCount = 1;
301
302 if (m_texLayoutPending) {
303 m_texLayoutPending = false;
304
305 barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
306 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
307 barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
308 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
309 barrier.image = m_texImage;
310
311 m_devFuncs->vkCmdPipelineBarrier(cb,
312 VK_PIPELINE_STAGE_HOST_BIT,
313 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
314 0, 0, nullptr, 0, nullptr,
315 1, &barrier);
316 } else {
317 m_texStagingPending = false;
318
319 barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
320 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
321 barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
322 barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
323 barrier.image = m_texStaging;
324 m_devFuncs->vkCmdPipelineBarrier(cb,
325 VK_PIPELINE_STAGE_HOST_BIT,
326 VK_PIPELINE_STAGE_TRANSFER_BIT,
327 0, 0, nullptr, 0, nullptr,
328 1, &barrier);
329
330 barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
331 barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
332 barrier.srcAccessMask = 0;
333 barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
334 barrier.image = m_texImage;
335 m_devFuncs->vkCmdPipelineBarrier(cb,
336 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
337 VK_PIPELINE_STAGE_TRANSFER_BIT,
338 0, 0, nullptr, 0, nullptr,
339 1, &barrier);
340
341 VkImageCopy copyInfo;
342 memset(s: &copyInfo, c: 0, n: sizeof(copyInfo));
343 copyInfo.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
344 copyInfo.srcSubresource.layerCount = 1;
345 copyInfo.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
346 copyInfo.dstSubresource.layerCount = 1;
347 copyInfo.extent.width = m_texSize.width();
348 copyInfo.extent.height = m_texSize.height();
349 copyInfo.extent.depth = 1;
350 m_devFuncs->vkCmdCopyImage(cb, m_texStaging, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
351 m_texImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copyInfo);
352
353 barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
354 barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
355 barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
356 barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
357 barrier.image = m_texImage;
358 m_devFuncs->vkCmdPipelineBarrier(cb,
359 VK_PIPELINE_STAGE_TRANSFER_BIT,
360 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
361 0, 0, nullptr, 0, nullptr,
362 1, &barrier);
363 }
364}
365
366void VulkanRenderer::initResources()
367{
368 qDebug(msg: "initResources");
369
370 VkDevice dev = m_window->device();
371 m_devFuncs = m_window->vulkanInstance()->deviceFunctions(device: dev);
372
373 // The setup is similar to hellovulkantriangle. The difference is the
374 // presence of a second vertex attribute (texcoord), a sampler, and that we
375 // need blending.
376
377 const int concurrentFrameCount = m_window->concurrentFrameCount();
378 const VkPhysicalDeviceLimits *pdevLimits = &m_window->physicalDeviceProperties()->limits;
379 const VkDeviceSize uniAlign = pdevLimits->minUniformBufferOffsetAlignment;
380 qDebug(msg: "uniform buffer offset alignment is %u", (uint) uniAlign);
381 VkBufferCreateInfo bufInfo;
382 memset(s: &bufInfo, c: 0, n: sizeof(bufInfo));
383 bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
384 // Our internal layout is vertex, uniform, uniform, ... with each uniform buffer start offset aligned to uniAlign.
385 const VkDeviceSize vertexAllocSize = aligned(v: sizeof(vertexData), byteAlign: uniAlign);
386 const VkDeviceSize uniformAllocSize = aligned(v: UNIFORM_DATA_SIZE, byteAlign: uniAlign);
387 bufInfo.size = vertexAllocSize + concurrentFrameCount * uniformAllocSize;
388 bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
389
390 VkResult err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_buf);
391 if (err != VK_SUCCESS)
392 qFatal(msg: "Failed to create buffer: %d", err);
393
394 VkMemoryRequirements memReq;
395 m_devFuncs->vkGetBufferMemoryRequirements(dev, m_buf, &memReq);
396
397 VkMemoryAllocateInfo memAllocInfo = {
398 .sType: VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
399 .pNext: nullptr,
400 .allocationSize: memReq.size,
401 .memoryTypeIndex: m_window->hostVisibleMemoryIndex()
402 };
403
404 err = m_devFuncs->vkAllocateMemory(dev, &memAllocInfo, nullptr, &m_bufMem);
405 if (err != VK_SUCCESS)
406 qFatal(msg: "Failed to allocate memory: %d", err);
407
408 err = m_devFuncs->vkBindBufferMemory(dev, m_buf, m_bufMem, 0);
409 if (err != VK_SUCCESS)
410 qFatal(msg: "Failed to bind buffer memory: %d", err);
411
412 quint8 *p;
413 err = m_devFuncs->vkMapMemory(dev, m_bufMem, 0, memReq.size, 0, reinterpret_cast<void **>(&p));
414 if (err != VK_SUCCESS)
415 qFatal(msg: "Failed to map memory: %d", err);
416 memcpy(dest: p, src: vertexData, n: sizeof(vertexData));
417 QMatrix4x4 ident;
418 memset(s: m_uniformBufInfo, c: 0, n: sizeof(m_uniformBufInfo));
419 for (int i = 0; i < concurrentFrameCount; ++i) {
420 const VkDeviceSize offset = vertexAllocSize + i * uniformAllocSize;
421 memcpy(dest: p + offset, src: ident.constData(), n: 16 * sizeof(float));
422 m_uniformBufInfo[i].buffer = m_buf;
423 m_uniformBufInfo[i].offset = offset;
424 m_uniformBufInfo[i].range = uniformAllocSize;
425 }
426 m_devFuncs->vkUnmapMemory(dev, m_bufMem);
427
428 VkVertexInputBindingDescription vertexBindingDesc = {
429 .binding: 0, // binding
430 .stride: 5 * sizeof(float),
431 .inputRate: VK_VERTEX_INPUT_RATE_VERTEX
432 };
433 VkVertexInputAttributeDescription vertexAttrDesc[] = {
434 { // position
435 .location: 0, // location
436 .binding: 0, // binding
437 .format: VK_FORMAT_R32G32B32_SFLOAT,
438 .offset: 0
439 },
440 { // texcoord
441 .location: 1,
442 .binding: 0,
443 .format: VK_FORMAT_R32G32_SFLOAT,
444 .offset: 3 * sizeof(float)
445 }
446 };
447
448 VkPipelineVertexInputStateCreateInfo vertexInputInfo;
449 vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
450 vertexInputInfo.pNext = nullptr;
451 vertexInputInfo.flags = 0;
452 vertexInputInfo.vertexBindingDescriptionCount = 1;
453 vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDesc;
454 vertexInputInfo.vertexAttributeDescriptionCount = 2;
455 vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc;
456
457 // Sampler.
458 VkSamplerCreateInfo samplerInfo;
459 memset(s: &samplerInfo, c: 0, n: sizeof(samplerInfo));
460 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
461 samplerInfo.magFilter = VK_FILTER_NEAREST;
462 samplerInfo.minFilter = VK_FILTER_NEAREST;
463 samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
464 samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
465 samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
466 samplerInfo.maxAnisotropy = 1.0f;
467 err = m_devFuncs->vkCreateSampler(dev, &samplerInfo, nullptr, &m_sampler);
468 if (err != VK_SUCCESS)
469 qFatal(msg: "Failed to create sampler: %d", err);
470
471 // Texture.
472 if (!createTexture(QStringLiteral(":/qt256.png")))
473 qFatal(msg: "Failed to create texture");
474
475 // Set up descriptor set and its layout.
476 VkDescriptorPoolSize descPoolSizes[2] = {
477 { .type: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, .descriptorCount: uint32_t(concurrentFrameCount) },
478 { .type: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount: uint32_t(concurrentFrameCount) }
479 };
480 VkDescriptorPoolCreateInfo descPoolInfo;
481 memset(s: &descPoolInfo, c: 0, n: sizeof(descPoolInfo));
482 descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
483 descPoolInfo.maxSets = concurrentFrameCount;
484 descPoolInfo.poolSizeCount = 2;
485 descPoolInfo.pPoolSizes = descPoolSizes;
486 err = m_devFuncs->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, &m_descPool);
487 if (err != VK_SUCCESS)
488 qFatal(msg: "Failed to create descriptor pool: %d", err);
489
490 VkDescriptorSetLayoutBinding layoutBinding[2] =
491 {
492 {
493 .binding: 0, // binding
494 .descriptorType: VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
495 .descriptorCount: 1, // descriptorCount
496 .stageFlags: VK_SHADER_STAGE_VERTEX_BIT,
497 .pImmutableSamplers: nullptr
498 },
499 {
500 .binding: 1, // binding
501 .descriptorType: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
502 .descriptorCount: 1, // descriptorCount
503 .stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT,
504 .pImmutableSamplers: nullptr
505 }
506 };
507 VkDescriptorSetLayoutCreateInfo descLayoutInfo = {
508 .sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
509 .pNext: nullptr,
510 .flags: 0,
511 .bindingCount: 2, // bindingCount
512 .pBindings: layoutBinding
513 };
514 err = m_devFuncs->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_descSetLayout);
515 if (err != VK_SUCCESS)
516 qFatal(msg: "Failed to create descriptor set layout: %d", err);
517
518 for (int i = 0; i < concurrentFrameCount; ++i) {
519 VkDescriptorSetAllocateInfo descSetAllocInfo = {
520 .sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
521 .pNext: nullptr,
522 .descriptorPool: m_descPool,
523 .descriptorSetCount: 1,
524 .pSetLayouts: &m_descSetLayout
525 };
526 err = m_devFuncs->vkAllocateDescriptorSets(dev, &descSetAllocInfo, &m_descSet[i]);
527 if (err != VK_SUCCESS)
528 qFatal(msg: "Failed to allocate descriptor set: %d", err);
529
530 VkWriteDescriptorSet descWrite[2];
531 memset(s: descWrite, c: 0, n: sizeof(descWrite));
532 descWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
533 descWrite[0].dstSet = m_descSet[i];
534 descWrite[0].dstBinding = 0;
535 descWrite[0].descriptorCount = 1;
536 descWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
537 descWrite[0].pBufferInfo = &m_uniformBufInfo[i];
538
539 VkDescriptorImageInfo descImageInfo = {
540 .sampler: m_sampler,
541 .imageView: m_texView,
542 .imageLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
543 };
544
545 descWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
546 descWrite[1].dstSet = m_descSet[i];
547 descWrite[1].dstBinding = 1;
548 descWrite[1].descriptorCount = 1;
549 descWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
550 descWrite[1].pImageInfo = &descImageInfo;
551
552 m_devFuncs->vkUpdateDescriptorSets(dev, 2, descWrite, 0, nullptr);
553 }
554
555 // Pipeline cache
556 VkPipelineCacheCreateInfo pipelineCacheInfo;
557 memset(s: &pipelineCacheInfo, c: 0, n: sizeof(pipelineCacheInfo));
558 pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
559 err = m_devFuncs->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &m_pipelineCache);
560 if (err != VK_SUCCESS)
561 qFatal(msg: "Failed to create pipeline cache: %d", err);
562
563 // Pipeline layout
564 VkPipelineLayoutCreateInfo pipelineLayoutInfo;
565 memset(s: &pipelineLayoutInfo, c: 0, n: sizeof(pipelineLayoutInfo));
566 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
567 pipelineLayoutInfo.setLayoutCount = 1;
568 pipelineLayoutInfo.pSetLayouts = &m_descSetLayout;
569 err = m_devFuncs->vkCreatePipelineLayout(dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout);
570 if (err != VK_SUCCESS)
571 qFatal(msg: "Failed to create pipeline layout: %d", err);
572
573 // Shaders
574 VkShaderModule vertShaderModule = createShader(QStringLiteral(":/texture_vert.spv"));
575 VkShaderModule fragShaderModule = createShader(QStringLiteral(":/texture_frag.spv"));
576
577 // Graphics pipeline
578 VkGraphicsPipelineCreateInfo pipelineInfo;
579 memset(s: &pipelineInfo, c: 0, n: sizeof(pipelineInfo));
580 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
581
582 VkPipelineShaderStageCreateInfo shaderStages[2] = {
583 {
584 .sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
585 .pNext: nullptr,
586 .flags: 0,
587 .stage: VK_SHADER_STAGE_VERTEX_BIT,
588 .module: vertShaderModule,
589 .pName: "main",
590 .pSpecializationInfo: nullptr
591 },
592 {
593 .sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
594 .pNext: nullptr,
595 .flags: 0,
596 .stage: VK_SHADER_STAGE_FRAGMENT_BIT,
597 .module: fragShaderModule,
598 .pName: "main",
599 .pSpecializationInfo: nullptr
600 }
601 };
602 pipelineInfo.stageCount = 2;
603 pipelineInfo.pStages = shaderStages;
604
605 pipelineInfo.pVertexInputState = &vertexInputInfo;
606
607 VkPipelineInputAssemblyStateCreateInfo ia;
608 memset(s: &ia, c: 0, n: sizeof(ia));
609 ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
610 ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
611 pipelineInfo.pInputAssemblyState = &ia;
612
613 // The viewport and scissor will be set dynamically via vkCmdSetViewport/Scissor.
614 // This way the pipeline does not need to be touched when resizing the window.
615 VkPipelineViewportStateCreateInfo vp;
616 memset(s: &vp, c: 0, n: sizeof(vp));
617 vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
618 vp.viewportCount = 1;
619 vp.scissorCount = 1;
620 pipelineInfo.pViewportState = &vp;
621
622 VkPipelineRasterizationStateCreateInfo rs;
623 memset(s: &rs, c: 0, n: sizeof(rs));
624 rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
625 rs.polygonMode = VK_POLYGON_MODE_FILL;
626 rs.cullMode = VK_CULL_MODE_BACK_BIT;
627 rs.frontFace = VK_FRONT_FACE_CLOCKWISE;
628 rs.lineWidth = 1.0f;
629 pipelineInfo.pRasterizationState = &rs;
630
631 VkPipelineMultisampleStateCreateInfo ms;
632 memset(s: &ms, c: 0, n: sizeof(ms));
633 ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
634 ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
635 pipelineInfo.pMultisampleState = &ms;
636
637 VkPipelineDepthStencilStateCreateInfo ds;
638 memset(s: &ds, c: 0, n: sizeof(ds));
639 ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
640 ds.depthTestEnable = VK_TRUE;
641 ds.depthWriteEnable = VK_TRUE;
642 ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
643 pipelineInfo.pDepthStencilState = &ds;
644
645 VkPipelineColorBlendStateCreateInfo cb;
646 memset(s: &cb, c: 0, n: sizeof(cb));
647 cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
648 // assume pre-multiplied alpha, blend, write out all of rgba
649 VkPipelineColorBlendAttachmentState att;
650 memset(s: &att, c: 0, n: sizeof(att));
651 att.colorWriteMask = 0xF;
652 att.blendEnable = VK_TRUE;
653 att.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
654 att.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
655 att.colorBlendOp = VK_BLEND_OP_ADD;
656 att.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
657 att.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
658 att.alphaBlendOp = VK_BLEND_OP_ADD;
659 cb.attachmentCount = 1;
660 cb.pAttachments = &att;
661 pipelineInfo.pColorBlendState = &cb;
662
663 VkDynamicState dynEnable[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
664 VkPipelineDynamicStateCreateInfo dyn;
665 memset(s: &dyn, c: 0, n: sizeof(dyn));
666 dyn.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
667 dyn.dynamicStateCount = sizeof(dynEnable) / sizeof(VkDynamicState);
668 dyn.pDynamicStates = dynEnable;
669 pipelineInfo.pDynamicState = &dyn;
670
671 pipelineInfo.layout = m_pipelineLayout;
672 pipelineInfo.renderPass = m_window->defaultRenderPass();
673
674 err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline);
675 if (err != VK_SUCCESS)
676 qFatal(msg: "Failed to create graphics pipeline: %d", err);
677
678 if (vertShaderModule)
679 m_devFuncs->vkDestroyShaderModule(dev, vertShaderModule, nullptr);
680 if (fragShaderModule)
681 m_devFuncs->vkDestroyShaderModule(dev, fragShaderModule, nullptr);
682}
683
684void VulkanRenderer::initSwapChainResources()
685{
686 qDebug(msg: "initSwapChainResources");
687
688 // Projection matrix
689 m_proj = m_window->clipCorrectionMatrix(); // adjust for Vulkan-OpenGL clip space differences
690 const QSize sz = m_window->swapChainImageSize();
691 m_proj.perspective(verticalAngle: 45.0f, aspectRatio: sz.width() / (float) sz.height(), nearPlane: 0.01f, farPlane: 100.0f);
692 m_proj.translate(x: 0, y: 0, z: -4);
693}
694
695void VulkanRenderer::releaseSwapChainResources()
696{
697 qDebug(msg: "releaseSwapChainResources");
698}
699
700void VulkanRenderer::releaseResources()
701{
702 qDebug(msg: "releaseResources");
703
704 VkDevice dev = m_window->device();
705
706 if (m_sampler) {
707 m_devFuncs->vkDestroySampler(dev, m_sampler, nullptr);
708 m_sampler = VK_NULL_HANDLE;
709 }
710
711 if (m_texStaging) {
712 m_devFuncs->vkDestroyImage(dev, m_texStaging, nullptr);
713 m_texStaging = VK_NULL_HANDLE;
714 }
715
716 if (m_texStagingMem) {
717 m_devFuncs->vkFreeMemory(dev, m_texStagingMem, nullptr);
718 m_texStagingMem = VK_NULL_HANDLE;
719 }
720
721 if (m_texView) {
722 m_devFuncs->vkDestroyImageView(dev, m_texView, nullptr);
723 m_texView = VK_NULL_HANDLE;
724 }
725
726 if (m_texImage) {
727 m_devFuncs->vkDestroyImage(dev, m_texImage, nullptr);
728 m_texImage = VK_NULL_HANDLE;
729 }
730
731 if (m_texMem) {
732 m_devFuncs->vkFreeMemory(dev, m_texMem, nullptr);
733 m_texMem = VK_NULL_HANDLE;
734 }
735
736 if (m_pipeline) {
737 m_devFuncs->vkDestroyPipeline(dev, m_pipeline, nullptr);
738 m_pipeline = VK_NULL_HANDLE;
739 }
740
741 if (m_pipelineLayout) {
742 m_devFuncs->vkDestroyPipelineLayout(dev, m_pipelineLayout, nullptr);
743 m_pipelineLayout = VK_NULL_HANDLE;
744 }
745
746 if (m_pipelineCache) {
747 m_devFuncs->vkDestroyPipelineCache(dev, m_pipelineCache, nullptr);
748 m_pipelineCache = VK_NULL_HANDLE;
749 }
750
751 if (m_descSetLayout) {
752 m_devFuncs->vkDestroyDescriptorSetLayout(dev, m_descSetLayout, nullptr);
753 m_descSetLayout = VK_NULL_HANDLE;
754 }
755
756 if (m_descPool) {
757 m_devFuncs->vkDestroyDescriptorPool(dev, m_descPool, nullptr);
758 m_descPool = VK_NULL_HANDLE;
759 }
760
761 if (m_buf) {
762 m_devFuncs->vkDestroyBuffer(dev, m_buf, nullptr);
763 m_buf = VK_NULL_HANDLE;
764 }
765
766 if (m_bufMem) {
767 m_devFuncs->vkFreeMemory(dev, m_bufMem, nullptr);
768 m_bufMem = VK_NULL_HANDLE;
769 }
770}
771
772void VulkanRenderer::startNextFrame()
773{
774 VkDevice dev = m_window->device();
775 VkCommandBuffer cb = m_window->currentCommandBuffer();
776 const QSize sz = m_window->swapChainImageSize();
777
778 // Add the necessary barriers and do the host-linear -> device-optimal copy, if not yet done.
779 ensureTexture();
780
781 VkClearColorValue clearColor = {.float32: { 0, 0, 0, 1 }};
782 VkClearDepthStencilValue clearDS = { .depth: 1, .stencil: 0 };
783 VkClearValue clearValues[2];
784 memset(s: clearValues, c: 0, n: sizeof(clearValues));
785 clearValues[0].color = clearColor;
786 clearValues[1].depthStencil = clearDS;
787
788 VkRenderPassBeginInfo rpBeginInfo;
789 memset(s: &rpBeginInfo, c: 0, n: sizeof(rpBeginInfo));
790 rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
791 rpBeginInfo.renderPass = m_window->defaultRenderPass();
792 rpBeginInfo.framebuffer = m_window->currentFramebuffer();
793 rpBeginInfo.renderArea.extent.width = sz.width();
794 rpBeginInfo.renderArea.extent.height = sz.height();
795 rpBeginInfo.clearValueCount = 2;
796 rpBeginInfo.pClearValues = clearValues;
797 VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
798 m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
799
800 quint8 *p;
801 VkResult err = m_devFuncs->vkMapMemory(dev, m_bufMem, m_uniformBufInfo[m_window->currentFrame()].offset,
802 UNIFORM_DATA_SIZE, 0, reinterpret_cast<void **>(&p));
803 if (err != VK_SUCCESS)
804 qFatal(msg: "Failed to map memory: %d", err);
805 QMatrix4x4 m = m_proj;
806 m.rotate(angle: m_rotation, x: 0, y: 0, z: 1);
807 memcpy(dest: p, src: m.constData(), n: 16 * sizeof(float));
808 m_devFuncs->vkUnmapMemory(dev, m_bufMem);
809
810 // Not exactly a real animation system, just advance on every frame for now.
811 m_rotation += 1.0f;
812
813 m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
814 m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1,
815 &m_descSet[m_window->currentFrame()], 0, nullptr);
816 VkDeviceSize vbOffset = 0;
817 m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_buf, &vbOffset);
818
819 VkViewport viewport;
820 viewport.x = viewport.y = 0;
821 viewport.width = sz.width();
822 viewport.height = sz.height();
823 viewport.minDepth = 0;
824 viewport.maxDepth = 1;
825 m_devFuncs->vkCmdSetViewport(cb, 0, 1, &viewport);
826
827 VkRect2D scissor;
828 scissor.offset.x = scissor.offset.y = 0;
829 scissor.extent.width = viewport.width;
830 scissor.extent.height = viewport.height;
831 m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor);
832
833 m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0);
834
835 m_devFuncs->vkCmdEndRenderPass(cmdBuf);
836
837 m_window->frameReady();
838 m_window->requestUpdate(); // render continuously, throttled by the presentation rate
839}
840

source code of qtbase/examples/vulkan/hellovulkantexture/hellovulkantexture.cpp