| 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. | 
| 62 | static 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 |  | 
| 70 | static const int UNIFORM_DATA_SIZE = 16 * sizeof(float); | 
| 71 |  | 
| 72 | static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign) | 
| 73 | { | 
| 74 |     return (v + byteAlign - 1) & ~(byteAlign - 1); | 
| 75 | } | 
| 76 |  | 
| 77 | QVulkanWindowRenderer *VulkanWindow::createRenderer() | 
| 78 | { | 
| 79 |     return new VulkanRenderer(this); | 
| 80 | } | 
| 81 |  | 
| 82 | VulkanRenderer::VulkanRenderer(QVulkanWindow *w) | 
| 83 |     : m_window(w) | 
| 84 | { | 
| 85 | } | 
| 86 |  | 
| 87 | VkShaderModule 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 |  | 
| 112 | bool 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 |  | 
| 197 | bool 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 |  | 
| 259 | bool 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 |  | 
| 288 | void 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: ©Info, 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, ©Info); | 
| 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 |  | 
| 366 | void 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 |  | 
| 684 | void 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 |  | 
| 695 | void VulkanRenderer::releaseSwapChainResources() | 
| 696 | { | 
| 697 |     qDebug(msg: "releaseSwapChainResources" ); | 
| 698 | } | 
| 699 |  | 
| 700 | void 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 |  | 
| 772 | void 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 |  |