Hello Vulkan Texture Vulkan Example▲
Sélectionnez
/**
**************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
***************************************************************************
*/
#include
"hellovulkantexture.h"
#include <QVulkanFunctions>
#include <QCoreApplication>
#include <QFile>
// Use a triangle strip to get a quad.
//
// Note that the vertex data and the projection matrix assume OpenGL. With
// Vulkan Y is negated in clip space and the near/far plane is at 0/1 instead
// of -1/1. These will be corrected for by an extra transformation when
// calculating the modelview-projection matrix.
static
float
vertexData[] =
{
// Y up, front = CW
// x, y, z, u, v
-
1
, -
1
, 0
, 0
, 1
,
-
1
, 1
, 0
, 0
, 0
,
1
, -
1
, 0
, 1
, 1
,
1
, 1
, 0
, 1
, 0
}
;
static
const
int
UNIFORM_DATA_SIZE =
16
*
sizeof
(float
);
static
inline
VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
{
return
(v +
byteAlign -
1
) &
amp; ~
(byteAlign -
1
);
}
QVulkanWindowRenderer *
VulkanWindow::
createRenderer()
{
return
new
VulkanRenderer(this
);
}
VulkanRenderer::
VulkanRenderer(QVulkanWindow *
w)
:
m_window(w)
{
}
VkShaderModule VulkanRenderer::
createShader(const
QString &
amp;name)
{
QFile file(name);
if
(!
file.open(QIODevice::
ReadOnly)) {
qWarning("Failed to read shader %s"
, qPrintable(name));
return
VK_NULL_HANDLE;
}
QByteArray blob =
file.readAll();
file.close();
VkShaderModuleCreateInfo shaderInfo;
memset(&
amp;shaderInfo, 0
, sizeof
(shaderInfo));
shaderInfo.sType =
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shaderInfo.codeSize =
blob.size();
shaderInfo.pCode =
reinterpret_cast
&
lt;const
uint32_t *&
gt;(blob.constData());
VkShaderModule shaderModule;
VkResult err =
m_devFuncs-&
gt;vkCreateShaderModule(m_window-&
gt;device(), &
amp;shaderInfo, nullptr
, &
amp;shaderModule);
if
(err !=
VK_SUCCESS) {
qWarning("Failed to create shader module: %d"
, err);
return
VK_NULL_HANDLE;
}
return
shaderModule;
}
bool
VulkanRenderer::
createTexture(const
QString &
amp;name)
{
QImage img(name);
if
(img.isNull()) {
qWarning("Failed to load image %s"
, qPrintable(name));
return
false
;
}
// Convert to byte ordered RGBA8. Use premultiplied alpha, see pColorBlendState in the pipeline.
img =
img.convertToFormat(QImage::
Format_RGBA8888_Premultiplied);
QVulkanFunctions *
f =
m_window-&
gt;vulkanInstance()-&
gt;functions();
VkDevice dev =
m_window-&
gt;device();
const
bool
srgb =
QCoreApplication::
arguments().contains(QStringLiteral("--srgb"
));
if
(srgb)
qDebug("sRGB swapchain was requested, making texture sRGB too"
);
m_texFormat =
srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
// Now we can either map and copy the image data directly, or have to go
// through a staging buffer to copy and convert into the internal optimal
// tiling format.
VkFormatProperties props;
f-&
gt;vkGetPhysicalDeviceFormatProperties(m_window-&
gt;physicalDevice(), m_texFormat, &
amp;props);
const
bool
canSampleLinear =
(props.linearTilingFeatures &
amp; VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
const
bool
canSampleOptimal =
(props.optimalTilingFeatures &
amp; VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
if
(!
canSampleLinear &
amp;&
amp; !
canSampleOptimal) {
qWarning("Neither linear nor optimal image sampling is supported for RGBA8"
);
return
false
;
}
static
bool
alwaysStage =
qEnvironmentVariableIntValue("QT_VK_FORCE_STAGE_TEX"
);
if
(canSampleLinear &
amp;&
amp; !
alwaysStage) {
if
(!
createTextureImage(img.size(), &
amp;m_texImage, &
amp;m_texMem,
VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_SAMPLED_BIT,
m_window-&
gt;hostVisibleMemoryIndex()))
return
false
;
if
(!
writeLinearImage(img, m_texImage, m_texMem))
return
false
;
m_texLayoutPending =
true
;
}
else
{
if
(!
createTextureImage(img.size(), &
amp;m_texStaging, &
amp;m_texStagingMem,
VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
m_window-&
gt;hostVisibleMemoryIndex()))
return
false
;
if
(!
createTextureImage(img.size(), &
amp;m_texImage, &
amp;m_texMem,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_SAMPLED_BIT,
m_window-&
gt;deviceLocalMemoryIndex()))
return
false
;
if
(!
writeLinearImage(img, m_texStaging, m_texStagingMem))
return
false
;
m_texStagingPending =
true
;
}
VkImageViewCreateInfo viewInfo;
memset(&
amp;viewInfo, 0
, sizeof
(viewInfo));
viewInfo.sType =
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image =
m_texImage;
viewInfo.viewType =
VK_IMAGE_VIEW_TYPE_2D;
viewInfo.format =
m_texFormat;
viewInfo.components.r =
VK_COMPONENT_SWIZZLE_R;
viewInfo.components.g =
VK_COMPONENT_SWIZZLE_G;
viewInfo.components.b =
VK_COMPONENT_SWIZZLE_B;
viewInfo.components.a =
VK_COMPONENT_SWIZZLE_A;
viewInfo.subresourceRange.aspectMask =
VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.levelCount =
viewInfo.subresourceRange.layerCount =
1
;
VkResult err =
m_devFuncs-&
gt;vkCreateImageView(dev, &
amp;viewInfo, nullptr
, &
amp;m_texView);
if
(err !=
VK_SUCCESS) {
qWarning("Failed to create image view for texture: %d"
, err);
return
false
;
}
m_texSize =
img.size();
return
true
;
}
bool
VulkanRenderer::
createTextureImage(const
QSize &
amp;size, VkImage *
image, VkDeviceMemory *
mem,
VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex)
{
VkDevice dev =
m_window-&
gt;device();
VkImageCreateInfo imageInfo;
memset(&
amp;imageInfo, 0
, sizeof
(imageInfo));
imageInfo.sType =
VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType =
VK_IMAGE_TYPE_2D;
imageInfo.format =
m_texFormat;
imageInfo.extent.width =
size.width();
imageInfo.extent.height =
size.height();
imageInfo.extent.depth =
1
;
imageInfo.mipLevels =
1
;
imageInfo.arrayLayers =
1
;
imageInfo.samples =
VK_SAMPLE_COUNT_1_BIT;
imageInfo.tiling =
tiling;
imageInfo.usage =
usage;
imageInfo.initialLayout =
VK_IMAGE_LAYOUT_PREINITIALIZED;
VkResult err =
m_devFuncs-&
gt;vkCreateImage(dev, &
amp;imageInfo, nullptr
, image);
if
(err !=
VK_SUCCESS) {
qWarning("Failed to create linear image for texture: %d"
, err);
return
false
;
}
VkMemoryRequirements memReq;
m_devFuncs-&
gt;vkGetImageMemoryRequirements(dev, *
image, &
amp;memReq);
if
(!
(memReq.memoryTypeBits &
amp; (1
&
lt;&
lt; memIndex))) {
VkPhysicalDeviceMemoryProperties physDevMemProps;
m_window-&
gt;vulkanInstance()-&
gt;functions()-&
gt;vkGetPhysicalDeviceMemoryProperties(m_window-&
gt;physicalDevice(), &
amp;physDevMemProps);
for
(uint32_t i =
0
; i &
lt; physDevMemProps.memoryTypeCount; ++
i) {
if
(!
(memReq.memoryTypeBits &
amp; (1
&
lt;&
lt; i)))
continue
;
memIndex =
i;
}
}
VkMemoryAllocateInfo allocInfo =
{
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
nullptr
,
memReq.size,
memIndex
}
;
qDebug("allocating %u bytes for texture image"
, uint32_t(memReq.size));
err =
m_devFuncs-&
gt;vkAllocateMemory(dev, &
amp;allocInfo, nullptr
, mem);
if
(err !=
VK_SUCCESS) {
qWarning("Failed to allocate memory for linear image: %d"
, err);
return
false
;
}
err =
m_devFuncs-&
gt;vkBindImageMemory(dev, *
image, *
mem, 0
);
if
(err !=
VK_SUCCESS) {
qWarning("Failed to bind linear image memory: %d"
, err);
return
false
;
}
return
true
;
}
bool
VulkanRenderer::
writeLinearImage(const
QImage &
amp;img, VkImage image, VkDeviceMemory memory)
{
VkDevice dev =
m_window-&
gt;device();
VkImageSubresource subres =
{
VK_IMAGE_ASPECT_COLOR_BIT,
0
, // mip level
0
}
;
VkSubresourceLayout layout;
m_devFuncs-&
gt;vkGetImageSubresourceLayout(dev, image, &
amp;subres, &
amp;layout);
uchar *
p;
VkResult err =
m_devFuncs-&
gt;vkMapMemory(dev, memory, layout.offset, layout.size, 0
, reinterpret_cast
&
lt;void
**&
gt;(&
amp;p));
if
(err !=
VK_SUCCESS) {
qWarning("Failed to map memory for linear image: %d"
, err);
return
false
;
}
for
(int
y =
0
; y &
lt; img.height(); ++
y) {
const
uchar *
line =
img.constScanLine(y);
memcpy(p, line, img.width() *
4
);
p +=
layout.rowPitch;
}
m_devFuncs-&
gt;vkUnmapMemory(dev, memory);
return
true
;
}
void
VulkanRenderer::
ensureTexture()
{
if
(!
m_texLayoutPending &
amp;&
amp; !
m_texStagingPending)
return
;
Q_ASSERT(m_texLayoutPending !=
m_texStagingPending);
VkCommandBuffer cb =
m_window-&
gt;currentCommandBuffer();
VkImageMemoryBarrier barrier;
memset(&
amp;barrier, 0
, sizeof
(barrier));
barrier.sType =
VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.subresourceRange.aspectMask =
VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.levelCount =
barrier.subresourceRange.layerCount =
1
;
if
(m_texLayoutPending) {
m_texLayoutPending =
false
;
barrier.oldLayout =
VK_IMAGE_LAYOUT_PREINITIALIZED;
barrier.newLayout =
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask =
VK_ACCESS_HOST_WRITE_BIT;
barrier.dstAccessMask =
VK_ACCESS_SHADER_READ_BIT;
barrier.image =
m_texImage;
m_devFuncs-&
gt;vkCmdPipelineBarrier(cb,
VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0
, 0
, nullptr
, 0
, nullptr
,
1
, &
amp;barrier);
}
else
{
m_texStagingPending =
false
;
barrier.oldLayout =
VK_IMAGE_LAYOUT_PREINITIALIZED;
barrier.newLayout =
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
barrier.srcAccessMask =
VK_ACCESS_HOST_WRITE_BIT;
barrier.dstAccessMask =
VK_ACCESS_TRANSFER_READ_BIT;
barrier.image =
m_texStaging;
m_devFuncs-&
gt;vkCmdPipelineBarrier(cb,
VK_PIPELINE_STAGE_HOST_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0
, 0
, nullptr
, 0
, nullptr
,
1
, &
amp;barrier);
barrier.oldLayout =
VK_IMAGE_LAYOUT_PREINITIALIZED;
barrier.newLayout =
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.srcAccessMask =
0
;
barrier.dstAccessMask =
VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.image =
m_texImage;
m_devFuncs-&
gt;vkCmdPipelineBarrier(cb,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0
, 0
, nullptr
, 0
, nullptr
,
1
, &
amp;barrier);
VkImageCopy copyInfo;
memset(&
amp;copyInfo, 0
, sizeof
(copyInfo));
copyInfo.srcSubresource.aspectMask =
VK_IMAGE_ASPECT_COLOR_BIT;
copyInfo.srcSubresource.layerCount =
1
;
copyInfo.dstSubresource.aspectMask =
VK_IMAGE_ASPECT_COLOR_BIT;
copyInfo.dstSubresource.layerCount =
1
;
copyInfo.extent.width =
m_texSize.width();
copyInfo.extent.height =
m_texSize.height();
copyInfo.extent.depth =
1
;
m_devFuncs-&
gt;vkCmdCopyImage(cb, m_texStaging, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
m_texImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1
, &
amp;copyInfo);
barrier.oldLayout =
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
barrier.newLayout =
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
barrier.srcAccessMask =
VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask =
VK_ACCESS_SHADER_READ_BIT;
barrier.image =
m_texImage;
m_devFuncs-&
gt;vkCmdPipelineBarrier(cb,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0
, 0
, nullptr
, 0
, nullptr
,
1
, &
amp;barrier);
}
}
void
VulkanRenderer::
initResources()
{
qDebug("initResources"
);
VkDevice dev =
m_window-&
gt;device();
m_devFuncs =
m_window-&
gt;vulkanInstance()-&
gt;deviceFunctions(dev);
// The setup is similar to hellovulkantriangle. The difference is the
// presence of a second vertex attribute (texcoord), a sampler, and that we
// need blending.
const
int
concurrentFrameCount =
m_window-&
gt;concurrentFrameCount();
const
VkPhysicalDeviceLimits *
pdevLimits =
&
amp;m_window-&
gt;physicalDeviceProperties()-&
gt;limits;
const
VkDeviceSize uniAlign =
pdevLimits-&
gt;minUniformBufferOffsetAlignment;
qDebug("uniform buffer offset alignment is %u"
, (uint) uniAlign);
VkBufferCreateInfo bufInfo;
memset(&
amp;bufInfo, 0
, sizeof
(bufInfo));
bufInfo.sType =
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
// Our internal layout is vertex, uniform, uniform, ... with each uniform buffer start offset aligned to uniAlign.
const
VkDeviceSize vertexAllocSize =
aligned(sizeof
(vertexData), uniAlign);
const
VkDeviceSize uniformAllocSize =
aligned(UNIFORM_DATA_SIZE, uniAlign);
bufInfo.size =
vertexAllocSize +
concurrentFrameCount *
uniformAllocSize;
bufInfo.usage =
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
VkResult err =
m_devFuncs-&
gt;vkCreateBuffer(dev, &
amp;bufInfo, nullptr
, &
amp;m_buf);
if
(err !=
VK_SUCCESS)
qFatal("Failed to create buffer: %d"
, err);
VkMemoryRequirements memReq;
m_devFuncs-&
gt;vkGetBufferMemoryRequirements(dev, m_buf, &
amp;memReq);
VkMemoryAllocateInfo memAllocInfo =
{
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
nullptr
,
memReq.size,
m_window-&
gt;hostVisibleMemoryIndex()
}
;
err =
m_devFuncs-&
gt;vkAllocateMemory(dev, &
amp;memAllocInfo, nullptr
, &
amp;m_bufMem);
if
(err !=
VK_SUCCESS)
qFatal("Failed to allocate memory: %d"
, err);
err =
m_devFuncs-&
gt;vkBindBufferMemory(dev, m_buf, m_bufMem, 0
);
if
(err !=
VK_SUCCESS)
qFatal("Failed to bind buffer memory: %d"
, err);
quint8 *
p;
err =
m_devFuncs-&
gt;vkMapMemory(dev, m_bufMem, 0
, memReq.size, 0
, reinterpret_cast
&
lt;void
**&
gt;(&
amp;p));
if
(err !=
VK_SUCCESS)
qFatal("Failed to map memory: %d"
, err);
memcpy(p, vertexData, sizeof
(vertexData));
QMatrix4x4 ident;
memset(m_uniformBufInfo, 0
, sizeof
(m_uniformBufInfo));
for
(int
i =
0
; i &
lt; concurrentFrameCount; ++
i) {
const
VkDeviceSize offset =
vertexAllocSize +
i *
uniformAllocSize;
memcpy(p +
offset, ident.constData(), 16
*
sizeof
(float
));
m_uniformBufInfo[i].buffer =
m_buf;
m_uniformBufInfo[i].offset =
offset;
m_uniformBufInfo[i].range =
uniformAllocSize;
}
m_devFuncs-&
gt;vkUnmapMemory(dev, m_bufMem);
VkVertexInputBindingDescription vertexBindingDesc =
{
0
, // binding
5
*
sizeof
(float
),
VK_VERTEX_INPUT_RATE_VERTEX
}
;
VkVertexInputAttributeDescription vertexAttrDesc[] =
{
{
// position
0
, // location
0
, // binding
VK_FORMAT_R32G32B32_SFLOAT,
0
}
,
{
// texcoord
1
,
0
,
VK_FORMAT_R32G32_SFLOAT,
3
*
sizeof
(float
)
}
}
;
VkPipelineVertexInputStateCreateInfo vertexInputInfo;
vertexInputInfo.sType =
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.pNext =
nullptr
;
vertexInputInfo.flags =
0
;
vertexInputInfo.vertexBindingDescriptionCount =
1
;
vertexInputInfo.pVertexBindingDescriptions =
&
amp;vertexBindingDesc;
vertexInputInfo.vertexAttributeDescriptionCount =
2
;
vertexInputInfo.pVertexAttributeDescriptions =
vertexAttrDesc;
// Sampler.
VkSamplerCreateInfo samplerInfo;
memset(&
amp;samplerInfo, 0
, sizeof
(samplerInfo));
samplerInfo.sType =
VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter =
VK_FILTER_NEAREST;
samplerInfo.minFilter =
VK_FILTER_NEAREST;
samplerInfo.addressModeU =
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV =
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW =
VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.maxAnisotropy =
1.0
f;
err =
m_devFuncs-&
gt;vkCreateSampler(dev, &
amp;samplerInfo, nullptr
, &
amp;m_sampler);
if
(err !=
VK_SUCCESS)
qFatal("Failed to create sampler: %d"
, err);
// Texture.
if
(!
createTexture(QStringLiteral(":/qt256.png"
)))
qFatal("Failed to create texture"
);
// Set up descriptor set and its layout.
VkDescriptorPoolSize descPoolSizes[2
] =
{
{
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uint32_t(concurrentFrameCount) }
,
{
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, uint32_t(concurrentFrameCount) }
}
;
VkDescriptorPoolCreateInfo descPoolInfo;
memset(&
amp;descPoolInfo, 0
, sizeof
(descPoolInfo));
descPoolInfo.sType =
VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descPoolInfo.maxSets =
concurrentFrameCount;
descPoolInfo.poolSizeCount =
2
;
descPoolInfo.pPoolSizes =
descPoolSizes;
err =
m_devFuncs-&
gt;vkCreateDescriptorPool(dev, &
amp;descPoolInfo, nullptr
, &
amp;m_descPool);
if
(err !=
VK_SUCCESS)
qFatal("Failed to create descriptor pool: %d"
, err);
VkDescriptorSetLayoutBinding layoutBinding[2
] =
{
{
0
, // binding
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1
, // descriptorCount
VK_SHADER_STAGE_VERTEX_BIT,
nullptr
}
,
{
1
, // binding
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
1
, // descriptorCount
VK_SHADER_STAGE_FRAGMENT_BIT,
nullptr
}
}
;
VkDescriptorSetLayoutCreateInfo descLayoutInfo =
{
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
nullptr
,
0
,
2
, // bindingCount
layoutBinding
}
;
err =
m_devFuncs-&
gt;vkCreateDescriptorSetLayout(dev, &
amp;descLayoutInfo, nullptr
, &
amp;m_descSetLayout);
if
(err !=
VK_SUCCESS)
qFatal("Failed to create descriptor set layout: %d"
, err);
for
(int
i =
0
; i &
lt; concurrentFrameCount; ++
i) {
VkDescriptorSetAllocateInfo descSetAllocInfo =
{
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
nullptr
,
m_descPool,
1
,
&
amp;m_descSetLayout
}
;
err =
m_devFuncs-&
gt;vkAllocateDescriptorSets(dev, &
amp;descSetAllocInfo, &
amp;m_descSet[i]);
if
(err !=
VK_SUCCESS)
qFatal("Failed to allocate descriptor set: %d"
, err);
VkWriteDescriptorSet descWrite[2
];
memset(descWrite, 0
, sizeof
(descWrite));
descWrite[0
].sType =
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descWrite[0
].dstSet =
m_descSet[i];
descWrite[0
].dstBinding =
0
;
descWrite[0
].descriptorCount =
1
;
descWrite[0
].descriptorType =
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descWrite[0
].pBufferInfo =
&
amp;m_uniformBufInfo[i];
VkDescriptorImageInfo descImageInfo =
{
m_sampler,
m_texView,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
}
;
descWrite[1
].sType =
VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descWrite[1
].dstSet =
m_descSet[i];
descWrite[1
].dstBinding =
1
;
descWrite[1
].descriptorCount =
1
;
descWrite[1
].descriptorType =
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
descWrite[1
].pImageInfo =
&
amp;descImageInfo;
m_devFuncs-&
gt;vkUpdateDescriptorSets(dev, 2
, descWrite, 0
, nullptr
);
}
// Pipeline cache
VkPipelineCacheCreateInfo pipelineCacheInfo;
memset(&
amp;pipelineCacheInfo, 0
, sizeof
(pipelineCacheInfo));
pipelineCacheInfo.sType =
VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
err =
m_devFuncs-&
gt;vkCreatePipelineCache(dev, &
amp;pipelineCacheInfo, nullptr
, &
amp;m_pipelineCache);
if
(err !=
VK_SUCCESS)
qFatal("Failed to create pipeline cache: %d"
, err);
// Pipeline layout
VkPipelineLayoutCreateInfo pipelineLayoutInfo;
memset(&
amp;pipelineLayoutInfo, 0
, sizeof
(pipelineLayoutInfo));
pipelineLayoutInfo.sType =
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount =
1
;
pipelineLayoutInfo.pSetLayouts =
&
amp;m_descSetLayout;
err =
m_devFuncs-&
gt;vkCreatePipelineLayout(dev, &
amp;pipelineLayoutInfo, nullptr
, &
amp;m_pipelineLayout);
if
(err !=
VK_SUCCESS)
qFatal("Failed to create pipeline layout: %d"
, err);
// Shaders
VkShaderModule vertShaderModule =
createShader(QStringLiteral(":/texture_vert.spv"
));
VkShaderModule fragShaderModule =
createShader(QStringLiteral(":/texture_frag.spv"
));
// Graphics pipeline
VkGraphicsPipelineCreateInfo pipelineInfo;
memset(&
amp;pipelineInfo, 0
, sizeof
(pipelineInfo));
pipelineInfo.sType =
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
VkPipelineShaderStageCreateInfo shaderStages[2
] =
{
{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr
,
0
,
VK_SHADER_STAGE_VERTEX_BIT,
vertShaderModule,
"main"
,
nullptr
}
,
{
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
nullptr
,
0
,
VK_SHADER_STAGE_FRAGMENT_BIT,
fragShaderModule,
"main"
,
nullptr
}
}
;
pipelineInfo.stageCount =
2
;
pipelineInfo.pStages =
shaderStages;
pipelineInfo.pVertexInputState =
&
amp;vertexInputInfo;
VkPipelineInputAssemblyStateCreateInfo ia;
memset(&
amp;ia, 0
, sizeof
(ia));
ia.sType =
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
ia.topology =
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
pipelineInfo.pInputAssemblyState =
&
amp;ia;
// The viewport and scissor will be set dynamically via vkCmdSetViewport/Scissor.
// This way the pipeline does not need to be touched when resizing the window.
VkPipelineViewportStateCreateInfo vp;
memset(&
amp;vp, 0
, sizeof
(vp));
vp.sType =
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
vp.viewportCount =
1
;
vp.scissorCount =
1
;
pipelineInfo.pViewportState =
&
amp;vp;
VkPipelineRasterizationStateCreateInfo rs;
memset(&
amp;rs, 0
, sizeof
(rs));
rs.sType =
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rs.polygonMode =
VK_POLYGON_MODE_FILL;
rs.cullMode =
VK_CULL_MODE_BACK_BIT;
rs.frontFace =
VK_FRONT_FACE_CLOCKWISE;
rs.lineWidth =
1.0
f;
pipelineInfo.pRasterizationState =
&
amp;rs;
VkPipelineMultisampleStateCreateInfo ms;
memset(&
amp;ms, 0
, sizeof
(ms));
ms.sType =
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
ms.rasterizationSamples =
VK_SAMPLE_COUNT_1_BIT;
pipelineInfo.pMultisampleState =
&
amp;ms;
VkPipelineDepthStencilStateCreateInfo ds;
memset(&
amp;ds, 0
, sizeof
(ds));
ds.sType =
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
ds.depthTestEnable =
VK_TRUE;
ds.depthWriteEnable =
VK_TRUE;
ds.depthCompareOp =
VK_COMPARE_OP_LESS_OR_EQUAL;
pipelineInfo.pDepthStencilState =
&
amp;ds;
VkPipelineColorBlendStateCreateInfo cb;
memset(&
amp;cb, 0
, sizeof
(cb));
cb.sType =
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
// assume pre-multiplied alpha, blend, write out all of rgba
VkPipelineColorBlendAttachmentState att;
memset(&
amp;att, 0
, sizeof
(att));
att.colorWriteMask =
0xF
;
att.blendEnable =
VK_TRUE;
att.srcColorBlendFactor =
VK_BLEND_FACTOR_ONE;
att.dstColorBlendFactor =
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
att.colorBlendOp =
VK_BLEND_OP_ADD;
att.srcAlphaBlendFactor =
VK_BLEND_FACTOR_ONE;
att.dstAlphaBlendFactor =
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
att.alphaBlendOp =
VK_BLEND_OP_ADD;
cb.attachmentCount =
1
;
cb.pAttachments =
&
amp;att;
pipelineInfo.pColorBlendState =
&
amp;cb;
VkDynamicState dynEnable[] =
{
VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }
;
VkPipelineDynamicStateCreateInfo dyn;
memset(&
amp;dyn, 0
, sizeof
(dyn));
dyn.sType =
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dyn.dynamicStateCount =
sizeof
(dynEnable) /
sizeof
(VkDynamicState);
dyn.pDynamicStates =
dynEnable;
pipelineInfo.pDynamicState =
&
amp;dyn;
pipelineInfo.layout =
m_pipelineLayout;
pipelineInfo.renderPass =
m_window-&
gt;defaultRenderPass();
err =
m_devFuncs-&
gt;vkCreateGraphicsPipelines(dev, m_pipelineCache, 1
, &
amp;pipelineInfo, nullptr
, &
amp;m_pipeline);
if
(err !=
VK_SUCCESS)
qFatal("Failed to create graphics pipeline: %d"
, err);
if
(vertShaderModule)
m_devFuncs-&
gt;vkDestroyShaderModule(dev, vertShaderModule, nullptr
);
if
(fragShaderModule)
m_devFuncs-&
gt;vkDestroyShaderModule(dev, fragShaderModule, nullptr
);
}
void
VulkanRenderer::
initSwapChainResources()
{
qDebug("initSwapChainResources"
);
// Projection matrix
m_proj =
m_window-&
gt;clipCorrectionMatrix(); // adjust for Vulkan-OpenGL clip space differences
const
QSize sz =
m_window-&
gt;swapChainImageSize();
m_proj.perspective(45.0
f, sz.width() /
(float
) sz.height(), 0.01
f, 100.0
f);
m_proj.translate(0
, 0
, -
4
);
}
void
VulkanRenderer::
releaseSwapChainResources()
{
qDebug("releaseSwapChainResources"
);
}
void
VulkanRenderer::
releaseResources()
{
qDebug("releaseResources"
);
VkDevice dev =
m_window-&
gt;device();
if
(m_sampler) {
m_devFuncs-&
gt;vkDestroySampler(dev, m_sampler, nullptr
);
m_sampler =
VK_NULL_HANDLE;
}
if
(m_texStaging) {
m_devFuncs-&
gt;vkDestroyImage(dev, m_texStaging, nullptr
);
m_texStaging =
VK_NULL_HANDLE;
}
if
(m_texStagingMem) {
m_devFuncs-&
gt;vkFreeMemory(dev, m_texStagingMem, nullptr
);
m_texStagingMem =
VK_NULL_HANDLE;
}
if
(m_texView) {
m_devFuncs-&
gt;vkDestroyImageView(dev, m_texView, nullptr
);
m_texView =
VK_NULL_HANDLE;
}
if
(m_texImage) {
m_devFuncs-&
gt;vkDestroyImage(dev, m_texImage, nullptr
);
m_texImage =
VK_NULL_HANDLE;
}
if
(m_texMem) {
m_devFuncs-&
gt;vkFreeMemory(dev, m_texMem, nullptr
);
m_texMem =
VK_NULL_HANDLE;
}
if
(m_pipeline) {
m_devFuncs-&
gt;vkDestroyPipeline(dev, m_pipeline, nullptr
);
m_pipeline =
VK_NULL_HANDLE;
}
if
(m_pipelineLayout) {
m_devFuncs-&
gt;vkDestroyPipelineLayout(dev, m_pipelineLayout, nullptr
);
m_pipelineLayout =
VK_NULL_HANDLE;
}
if
(m_pipelineCache) {
m_devFuncs-&
gt;vkDestroyPipelineCache(dev, m_pipelineCache, nullptr
);
m_pipelineCache =
VK_NULL_HANDLE;
}
if
(m_descSetLayout) {
m_devFuncs-&
gt;vkDestroyDescriptorSetLayout(dev, m_descSetLayout, nullptr
);
m_descSetLayout =
VK_NULL_HANDLE;
}
if
(m_descPool) {
m_devFuncs-&
gt;vkDestroyDescriptorPool(dev, m_descPool, nullptr
);
m_descPool =
VK_NULL_HANDLE;
}
if
(m_buf) {
m_devFuncs-&
gt;vkDestroyBuffer(dev, m_buf, nullptr
);
m_buf =
VK_NULL_HANDLE;
}
if
(m_bufMem) {
m_devFuncs-&
gt;vkFreeMemory(dev, m_bufMem, nullptr
);
m_bufMem =
VK_NULL_HANDLE;
}
}
void
VulkanRenderer::
startNextFrame()
{
VkDevice dev =
m_window-&
gt;device();
VkCommandBuffer cb =
m_window-&
gt;currentCommandBuffer();
const
QSize sz =
m_window-&
gt;swapChainImageSize();
// Add the necessary barriers and do the host-linear -> device-optimal copy, if not yet done.
ensureTexture();
VkClearColorValue clearColor =
{{
0
, 0
, 0
, 1
}}
;
VkClearDepthStencilValue clearDS =
{
1
, 0
}
;
VkClearValue clearValues[2
];
memset(clearValues, 0
, sizeof
(clearValues));
clearValues[0
].color =
clearColor;
clearValues[1
].depthStencil =
clearDS;
VkRenderPassBeginInfo rpBeginInfo;
memset(&
amp;rpBeginInfo, 0
, sizeof
(rpBeginInfo));
rpBeginInfo.sType =
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rpBeginInfo.renderPass =
m_window-&
gt;defaultRenderPass();
rpBeginInfo.framebuffer =
m_window-&
gt;currentFramebuffer();
rpBeginInfo.renderArea.extent.width =
sz.width();
rpBeginInfo.renderArea.extent.height =
sz.height();
rpBeginInfo.clearValueCount =
2
;
rpBeginInfo.pClearValues =
clearValues;
VkCommandBuffer cmdBuf =
m_window-&
gt;currentCommandBuffer();
m_devFuncs-&
gt;vkCmdBeginRenderPass(cmdBuf, &
amp;rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
quint8 *
p;
VkResult err =
m_devFuncs-&
gt;vkMapMemory(dev, m_bufMem, m_uniformBufInfo[m_window-&
gt;currentFrame()].offset,
UNIFORM_DATA_SIZE, 0
, reinterpret_cast
&
lt;void
**&
gt;(&
amp;p));
if
(err !=
VK_SUCCESS)
qFatal("Failed to map memory: %d"
, err);
QMatrix4x4 m =
m_proj;
m.rotate(m_rotation, 0
, 0
, 1
);
memcpy(p, m.constData(), 16
*
sizeof
(float
));
m_devFuncs-&
gt;vkUnmapMemory(dev, m_bufMem);
// Not exactly a real animation system, just advance on every frame for now.
m_rotation +=
1.0
f;
m_devFuncs-&
gt;vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
m_devFuncs-&
gt;vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0
, 1
,
&
amp;m_descSet[m_window-&
gt;currentFrame()], 0
, nullptr
);
VkDeviceSize vbOffset =
0
;
m_devFuncs-&
gt;vkCmdBindVertexBuffers(cb, 0
, 1
, &
amp;m_buf, &
amp;vbOffset);
VkViewport viewport;
viewport.x =
viewport.y =
0
;
viewport.width =
sz.width();
viewport.height =
sz.height();
viewport.minDepth =
0
;
viewport.maxDepth =
1
;
m_devFuncs-&
gt;vkCmdSetViewport(cb, 0
, 1
, &
amp;viewport);
VkRect2D scissor;
scissor.offset.x =
scissor.offset.y =
0
;
scissor.extent.width =
viewport.width;
scissor.extent.height =
viewport.height;
m_devFuncs-&
gt;vkCmdSetScissor(cb, 0
, 1
, &
amp;scissor);
m_devFuncs-&
gt;vkCmdDraw(cb, 4
, 1
, 0
, 0
);
m_devFuncs-&
gt;vkCmdEndRenderPass(cmdBuf);
m_window-&
gt;frameReady();
m_window-&
gt;requestUpdate(); // render continuously, throttled by the presentation rate
}