Introduce QVulkanWindow

A convenience subclass of QWindow that provides a Vulkan-capable
window with a double-buffered FIFO swapchain.

While advanced use cases are better served by a custom QWindow
subclass, many applications can benefit from having a convenient
helper that makes getting started easier.

Add also three examples of increasing complexity, and a variant that
shows embeddeding into widgets via QWindowContainer.

[ChangeLog][QtGui] Added QVulkanWindow, a convenience subclass of
QWindow.

Task-number: QTBUG-55981
Change-Id: I6cdc9ff1390ac6258e278377233fd369a0bfeddc
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2017-02-12 15:08:52 +01:00
parent 66e162e8f1
commit 8d72aba9e3
43 changed files with 6076 additions and 6 deletions

View File

@ -15,7 +15,10 @@ SUBDIRS = \
widgets \
xml
qtHaveModule(gui):qtConfig(opengl): SUBDIRS += opengl
qtHaveModule(gui) {
qtConfig(opengl): SUBDIRS += opengl
qtConfig(vulkan): SUBDIRS += vulkan
}
aggregate.files = aggregate/examples.pro
aggregate.path = $$[QT_INSTALL_EXAMPLES]

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,41 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** 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.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example hellovulkantexture
\ingroup examples-vulkan
\title Hello Vulkan Texture Vulkan Example
\brief Shows the basics of rendering with textures in a QVulkanWindow
The \e{Hello Vulkan Texture Example} builds on \l hellovulkantriangle. Here
instead of drawing a single triangle, a triangle strip is drawn in order to
get a quad on the screen. This is then textured using a QImage loaded from
a .png image file.
\image hellovulkantexture.png
\include examples-run.qdocinc
*/

View File

@ -0,0 +1,49 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** 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.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example hellovulkantriangle
\ingroup examples-vulkan
\title Hello Vulkan Triangle Example
\brief Shows the basics of rendering with QVulkanWindow and the Vulkan API
The \e{Hello Vulkan Triangle Example} builds on \l hellovulkanwindow. This
time a full graphics pipeline is created, including a vertex and fragment
shader. This pipeline is then used to render a triangle.
\image hellovulkantriangle.png
The example also demonstrates multisample antialiasing. Based on the
supported sample counts reported by QVulkanWindow::supportedSampleCounts()
the example chooses between 8x, 4x, or no multisampling. Once configured
via QVulkanWindow::setSamples(), QVulkanWindow takes care of the rest: the
additional multisample color buffers are created automatically, and
resolving into the swapchain buffers is performed at the end of the default
render pass for each frame.
\include examples-run.qdocinc
*/

View File

@ -0,0 +1,49 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** 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.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example hellovulkanwidget
\ingroup examples-vulkan
\title Hello Vulkan Widget Example
\brief Shows the usage of QVulkanWindow in QWidget applications
The \e{Hello Vulkan Widget Example} is a variant of \l hellovulkantriangle
that embeds the QVulkanWindow into a QWidget-based user interface using
QWidget::createWindowContainer().
\image hellovulkanwidget.png
The code to set up the Vulkan pipeline and render the triangle is the same
as in \l hellovulkantriangle. In addition, this example demonstrates
another feature of QVulkanWindow: reading the image content back from the
color buffer into a QImage. By clicking the Grab button, the example
renders the next frame and follows it up with a transfer operation in order
to get the swapchain color buffer content copied into host accessible
memory. The image is then saved to disk via QImage::save().
\include examples-run.qdocinc
*/

View File

@ -0,0 +1,101 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** 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.
**
** GNU Free Documentation License Usage
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of
** this file. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example hellovulkanwindow
\title Hello Vulkan Window Example
\ingroup examples-vulkan
\brief Shows the basics of using QVulkanWindow
The \e{Hello Vulkan Window Example} shows the basics of using QVulkanWindow
in order to display rendering with the Vulkan graphics API on systems that
support this.
\image hellovulkanwindow.png
In this example there will be no actual rendering: it simply begins and
ends a render pass, which results in clearing the buffers to a fixed value.
The color buffer clear value changes on every frame.
\section1 Startup
Each Qt application using Vulkan will have to have a \c{Vulkan instance}
which encapsulates application-level state and initializes a Vulkan library.
A QVulkanWindow must always be associated with a QVulkanInstance and hence
the example performs instance creation before the window. The
QVulkanInstance object must also outlive the window.
\snippet hellovulkanwindow/main.cpp 0
The example enables validation layers, when supported. When the requested
layers are not present, the request will be ignored. Additional layers and
extensions can be enabled in a similar manner.
\snippet hellovulkanwindow/main.cpp 1
Once the instance is ready, it is time to create a window. Note that \c w
lives on the stack and is declared after \c inst.
\section1 The QVulkanWindow Subclass
To add custom functionality to a QVulkanWindow, subclassing is used. This
follows the existing patterns from QOpenGLWindow and QOpenGLWidget.
However, QVulkanWindow utilizes a separate QVulkanWindowRenderer object.
This resembles QQuickFramebufferObject, and allows better separation of the
functions that are supposed to be reimplemented.
\snippet hellovulkanwindow/hellovulkanwindow.h 0
The QVulkanWindow subclass reimplements the factory function
QVulkanWindow::createRenderer(). This simply returns a new instance of the
QVulkanWindowRenderer subclass. In order to be able to access various
Vulkan resources via the window object, a pointer to the window is passed
and stored via the constructor.
\snippet hellovulkanwindow/hellovulkanwindow.cpp 0
Graphics resource creation and destruction is typically done in one of the
init - resource functions.
\snippet hellovulkanwindow/hellovulkanwindow.cpp 1
\section1 The Actual Rendering
QVulkanWindow subclasses queue their draw calls in their reimplementation
of QVulkanWindowRenderer::startNextFrame(). Once done, they are required to
call back QVulkanWindow::frameReady(). The example has no asynchronous
command generation, so the frameReady() call is made directly from
startNextFrame().
\snippet hellovulkanwindow/hellovulkanwindow.cpp 2
To get continuous updates, the example simply invokes
QWindow::requestUpdate() in order to schedule a repaint.
\include examples-run.qdocinc
*/

View File

@ -0,0 +1,828 @@
/****************************************************************************
**
** 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[] = {
// 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) & ~(byteAlign - 1);
}
QVulkanWindowRenderer *VulkanWindow::createRenderer()
{
return new VulkanRenderer(this);
}
VulkanRenderer::VulkanRenderer(QVulkanWindow *w)
: m_window(w)
{
}
VkShaderModule VulkanRenderer::createShader(const QString &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(&shaderInfo, 0, sizeof(shaderInfo));
shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shaderInfo.codeSize = blob.size();
shaderInfo.pCode = reinterpret_cast<const uint32_t *>(blob.constData());
VkShaderModule shaderModule;
VkResult err = m_devFuncs->vkCreateShaderModule(m_window->device(), &shaderInfo, nullptr, &shaderModule);
if (err != VK_SUCCESS) {
qWarning("Failed to create shader module: %d", err);
return VK_NULL_HANDLE;
}
return shaderModule;
}
bool VulkanRenderer::createTexture(const QString &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->vulkanInstance()->functions();
VkDevice dev = m_window->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->vkGetPhysicalDeviceFormatProperties(m_window->physicalDevice(), m_texFormat, &props);
const bool canSampleLinear = (props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
if (!canSampleLinear && !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 && !alwaysStage) {
if (!createTextureImage(img.size(), &m_texImage, &m_texMem,
VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_SAMPLED_BIT,
m_window->hostVisibleMemoryIndex()))
return false;
if (!writeLinearImage(img, m_texImage, m_texMem))
return false;
m_texLayoutPending = true;
} else {
if (!createTextureImage(img.size(), &m_texStaging, &m_texStagingMem,
VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
m_window->hostVisibleMemoryIndex()))
return false;
if (!createTextureImage(img.size(), &m_texImage, &m_texMem,
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
m_window->deviceLocalMemoryIndex()))
return false;
if (!writeLinearImage(img, m_texStaging, m_texStagingMem))
return false;
m_texStagingPending = true;
}
VkImageViewCreateInfo viewInfo;
memset(&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->vkCreateImageView(dev, &viewInfo, nullptr, &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 &size, VkImage *image, VkDeviceMemory *mem,
VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex)
{
VkDevice dev = m_window->device();
VkImageCreateInfo imageInfo;
memset(&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->vkCreateImage(dev, &imageInfo, nullptr, image);
if (err != VK_SUCCESS) {
qWarning("Failed to create linear image for texture: %d", err);
return false;
}
VkMemoryRequirements memReq;
m_devFuncs->vkGetImageMemoryRequirements(dev, *image, &memReq);
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->vkAllocateMemory(dev, &allocInfo, nullptr, mem);
if (err != VK_SUCCESS) {
qWarning("Failed to allocate memory for linear image: %d", err);
return false;
}
err = m_devFuncs->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 &img, VkImage image, VkDeviceMemory memory)
{
VkDevice dev = m_window->device();
VkImageSubresource subres = {
VK_IMAGE_ASPECT_COLOR_BIT,
0, // mip level
0
};
VkSubresourceLayout layout;
m_devFuncs->vkGetImageSubresourceLayout(dev, image, &subres, &layout);
uchar *p;
VkResult err = m_devFuncs->vkMapMemory(dev, memory, layout.offset, layout.size, 0, reinterpret_cast<void **>(&p));
if (err != VK_SUCCESS) {
qWarning("Failed to map memory for linear image: %d", err);
return false;
}
for (int y = 0; y < img.height(); ++y) {
const uchar *line = img.constScanLine(y);
memcpy(p, line, img.width() * 4);
p += layout.rowPitch;
}
m_devFuncs->vkUnmapMemory(dev, memory);
return true;
}
void VulkanRenderer::ensureTexture()
{
if (!m_texLayoutPending && !m_texStagingPending)
return;
Q_ASSERT(m_texLayoutPending != m_texStagingPending);
VkCommandBuffer cb = m_window->currentCommandBuffer();
VkImageMemoryBarrier barrier;
memset(&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->vkCmdPipelineBarrier(cb,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 0, nullptr, 0, nullptr,
1, &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->vkCmdPipelineBarrier(cb,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 0, nullptr,
1, &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->vkCmdPipelineBarrier(cb,
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_TRANSFER_BIT,
0, 0, nullptr, 0, nullptr,
1, &barrier);
VkImageCopy copyInfo;
memset(&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->vkCmdCopyImage(cb, m_texStaging, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
m_texImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &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->vkCmdPipelineBarrier(cb,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 0, nullptr, 0, nullptr,
1, &barrier);
}
}
void VulkanRenderer::initResources()
{
qDebug("initResources");
VkDevice dev = m_window->device();
m_devFuncs = m_window->vulkanInstance()->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->concurrentFrameCount();
const VkPhysicalDeviceLimits *pdevLimits = &m_window->physicalDeviceProperties()->limits;
const VkDeviceSize uniAlign = pdevLimits->minUniformBufferOffsetAlignment;
qDebug("uniform buffer offset alignment is %u", (uint) uniAlign);
VkBufferCreateInfo bufInfo;
memset(&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->vkCreateBuffer(dev, &bufInfo, nullptr, &m_buf);
if (err != VK_SUCCESS)
qFatal("Failed to create buffer: %d", err);
VkMemoryRequirements memReq;
m_devFuncs->vkGetBufferMemoryRequirements(dev, m_buf, &memReq);
VkMemoryAllocateInfo memAllocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
nullptr,
memReq.size,
m_window->hostVisibleMemoryIndex()
};
err = m_devFuncs->vkAllocateMemory(dev, &memAllocInfo, nullptr, &m_bufMem);
if (err != VK_SUCCESS)
qFatal("Failed to allocate memory: %d", err);
err = m_devFuncs->vkBindBufferMemory(dev, m_buf, m_bufMem, 0);
if (err != VK_SUCCESS)
qFatal("Failed to bind buffer memory: %d", err);
quint8 *p;
err = m_devFuncs->vkMapMemory(dev, m_bufMem, 0, memReq.size, 0, reinterpret_cast<void **>(&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 < 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->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 = &vertexBindingDesc;
vertexInputInfo.vertexAttributeDescriptionCount = 2;
vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc;
// Sampler.
VkSamplerCreateInfo samplerInfo;
memset(&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;
err = m_devFuncs->vkCreateSampler(dev, &samplerInfo, nullptr, &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(&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->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, &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->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_descSetLayout);
if (err != VK_SUCCESS)
qFatal("Failed to create descriptor set layout: %d", err);
for (int i = 0; i < concurrentFrameCount; ++i) {
VkDescriptorSetAllocateInfo descSetAllocInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
nullptr,
m_descPool,
1,
&m_descSetLayout
};
err = m_devFuncs->vkAllocateDescriptorSets(dev, &descSetAllocInfo, &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 = &m_uniformBufInfo[i];
VkDescriptorImageInfo descImageInfo = {
m_sampler,
m_texView,
VK_IMAGE_LAYOUT_GENERAL
};
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 = &descImageInfo;
m_devFuncs->vkUpdateDescriptorSets(dev, 2, descWrite, 0, nullptr);
}
// Pipeline cache
VkPipelineCacheCreateInfo pipelineCacheInfo;
memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo));
pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
err = m_devFuncs->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &m_pipelineCache);
if (err != VK_SUCCESS)
qFatal("Failed to create pipeline cache: %d", err);
// Pipeline layout
VkPipelineLayoutCreateInfo pipelineLayoutInfo;
memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &m_descSetLayout;
err = m_devFuncs->vkCreatePipelineLayout(dev, &pipelineLayoutInfo, nullptr, &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(&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 = &vertexInputInfo;
VkPipelineInputAssemblyStateCreateInfo ia;
memset(&ia, 0, sizeof(ia));
ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
pipelineInfo.pInputAssemblyState = &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(&vp, 0, sizeof(vp));
vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
vp.viewportCount = 1;
vp.scissorCount = 1;
pipelineInfo.pViewportState = &vp;
VkPipelineRasterizationStateCreateInfo rs;
memset(&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_NONE; // we want the back face as well
rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rs.lineWidth = 1.0f;
pipelineInfo.pRasterizationState = &rs;
VkPipelineMultisampleStateCreateInfo ms;
memset(&ms, 0, sizeof(ms));
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
pipelineInfo.pMultisampleState = &ms;
VkPipelineDepthStencilStateCreateInfo ds;
memset(&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 = &ds;
VkPipelineColorBlendStateCreateInfo cb;
memset(&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(&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 = &att;
pipelineInfo.pColorBlendState = &cb;
VkDynamicState dynEnable[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dyn;
memset(&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 = &dyn;
pipelineInfo.layout = m_pipelineLayout;
pipelineInfo.renderPass = m_window->defaultRenderPass();
err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline);
if (err != VK_SUCCESS)
qFatal("Failed to create graphics pipeline: %d", err);
if (vertShaderModule)
m_devFuncs->vkDestroyShaderModule(dev, vertShaderModule, nullptr);
if (fragShaderModule)
m_devFuncs->vkDestroyShaderModule(dev, fragShaderModule, nullptr);
}
void VulkanRenderer::initSwapChainResources()
{
qDebug("initSwapChainResources");
// Projection matrix
m_proj = *m_window->clipCorrectionMatrix(); // adjust for Vulkan-OpenGL clip space differences
const QSize sz = m_window->swapChainImageSize();
m_proj.perspective(45.0f, sz.width() / (float) sz.height(), 0.01f, 100.0f);
m_proj.translate(0, 0, -4);
}
void VulkanRenderer::releaseSwapChainResources()
{
qDebug("releaseSwapChainResources");
}
void VulkanRenderer::releaseResources()
{
qDebug("releaseResources");
VkDevice dev = m_window->device();
if (m_sampler) {
m_devFuncs->vkDestroySampler(dev, m_sampler, nullptr);
m_sampler = VK_NULL_HANDLE;
}
if (m_texStaging) {
m_devFuncs->vkDestroyImage(dev, m_texStaging, nullptr);
m_texStaging = VK_NULL_HANDLE;
}
if (m_texStagingMem) {
m_devFuncs->vkFreeMemory(dev, m_texStagingMem, nullptr);
m_texStagingMem = VK_NULL_HANDLE;
}
if (m_texView) {
m_devFuncs->vkDestroyImageView(dev, m_texView, nullptr);
m_texView = VK_NULL_HANDLE;
}
if (m_texImage) {
m_devFuncs->vkDestroyImage(dev, m_texImage, nullptr);
m_texImage = VK_NULL_HANDLE;
}
if (m_texMem) {
m_devFuncs->vkFreeMemory(dev, m_texMem, nullptr);
m_texMem = VK_NULL_HANDLE;
}
if (m_pipeline) {
m_devFuncs->vkDestroyPipeline(dev, m_pipeline, nullptr);
m_pipeline = VK_NULL_HANDLE;
}
if (m_pipelineLayout) {
m_devFuncs->vkDestroyPipelineLayout(dev, m_pipelineLayout, nullptr);
m_pipelineLayout = VK_NULL_HANDLE;
}
if (m_pipelineCache) {
m_devFuncs->vkDestroyPipelineCache(dev, m_pipelineCache, nullptr);
m_pipelineCache = VK_NULL_HANDLE;
}
if (m_descSetLayout) {
m_devFuncs->vkDestroyDescriptorSetLayout(dev, m_descSetLayout, nullptr);
m_descSetLayout = VK_NULL_HANDLE;
}
if (m_descPool) {
m_devFuncs->vkDestroyDescriptorPool(dev, m_descPool, nullptr);
m_descPool = VK_NULL_HANDLE;
}
if (m_buf) {
m_devFuncs->vkDestroyBuffer(dev, m_buf, nullptr);
m_buf = VK_NULL_HANDLE;
}
if (m_bufMem) {
m_devFuncs->vkFreeMemory(dev, m_bufMem, nullptr);
m_bufMem = VK_NULL_HANDLE;
}
}
void VulkanRenderer::startNextFrame()
{
VkDevice dev = m_window->device();
VkCommandBuffer cb = m_window->currentCommandBuffer();
const QSize sz = m_window->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(&rpBeginInfo, 0, sizeof(rpBeginInfo));
rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rpBeginInfo.renderPass = m_window->defaultRenderPass();
rpBeginInfo.framebuffer = m_window->currentFramebuffer();
rpBeginInfo.renderArea.extent.width = sz.width();
rpBeginInfo.renderArea.extent.height = sz.height();
rpBeginInfo.clearValueCount = 2;
rpBeginInfo.pClearValues = clearValues;
VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
quint8 *p;
VkResult err = m_devFuncs->vkMapMemory(dev, m_bufMem, m_uniformBufInfo[m_window->currentFrame()].offset,
UNIFORM_DATA_SIZE, 0, reinterpret_cast<void **>(&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->vkUnmapMemory(dev, m_bufMem);
// Not exactly a real animation system, just advance on every frame for now.
m_rotation += 1.0f;
m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1,
&m_descSet[m_window->currentFrame()], 0, nullptr);
VkDeviceSize vbOffset = 0;
m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_buf, &vbOffset);
VkViewport viewport;
viewport.x = viewport.y = 0;
viewport.width = sz.width();
viewport.height = sz.height();
viewport.minDepth = 0;
viewport.maxDepth = 1;
m_devFuncs->vkCmdSetViewport(cb, 0, 1, &viewport);
VkRect2D scissor;
scissor.offset.x = scissor.offset.y = 0;
scissor.extent.width = viewport.width;
scissor.extent.height = viewport.height;
m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor);
m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0);
m_devFuncs->vkCmdEndRenderPass(cmdBuf);
m_window->frameReady();
m_window->requestUpdate(); // render continuously, throttled by the presentation rate
}

View File

@ -0,0 +1,108 @@
/****************************************************************************
**
** 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 <QVulkanWindow>
#include <QImage>
class VulkanRenderer : public QVulkanWindowRenderer
{
public:
VulkanRenderer(QVulkanWindow *w);
void initResources() override;
void initSwapChainResources() override;
void releaseSwapChainResources() override;
void releaseResources() override;
void startNextFrame() override;
private:
VkShaderModule createShader(const QString &name);
bool createTexture(const QString &name);
bool createTextureImage(const QSize &size, VkImage *image, VkDeviceMemory *mem,
VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex);
bool writeLinearImage(const QImage &img, VkImage image, VkDeviceMemory memory);
void ensureTexture();
QVulkanWindow *m_window;
QVulkanDeviceFunctions *m_devFuncs;
VkDeviceMemory m_bufMem = VK_NULL_HANDLE;
VkBuffer m_buf = VK_NULL_HANDLE;
VkDescriptorBufferInfo m_uniformBufInfo[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
VkDescriptorPool m_descPool = VK_NULL_HANDLE;
VkDescriptorSetLayout m_descSetLayout = VK_NULL_HANDLE;
VkDescriptorSet m_descSet[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
VkPipelineCache m_pipelineCache = VK_NULL_HANDLE;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
VkPipeline m_pipeline = VK_NULL_HANDLE;
VkSampler m_sampler = VK_NULL_HANDLE;
VkImage m_texImage = VK_NULL_HANDLE;
VkDeviceMemory m_texMem = VK_NULL_HANDLE;
bool m_texLayoutPending = false;
VkImageView m_texView = VK_NULL_HANDLE;
VkImage m_texStaging = VK_NULL_HANDLE;
VkDeviceMemory m_texStagingMem = VK_NULL_HANDLE;
bool m_texStagingPending = false;
QSize m_texSize;
VkFormat m_texFormat;
QMatrix4x4 m_proj;
float m_rotation = 0.0f;
};
class VulkanWindow : public QVulkanWindow
{
public:
QVulkanWindowRenderer *createRenderer() override;
};

View File

@ -0,0 +1,7 @@
HEADERS += hellovulkantexture.h
SOURCES += hellovulkantexture.cpp main.cpp
RESOURCES += hellovulkantexture.qrc
# install
target.path = $$[QT_INSTALL_EXAMPLES]/vulkan/hellovulkantexture
INSTALLS += target

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file>texture_vert.spv</file>
<file>texture_frag.spv</file>
<file>qt256.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,91 @@
/****************************************************************************
**
** 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 <QGuiApplication>
#include <QVulkanInstance>
#include <QLoggingCategory>
#include "hellovulkantexture.h"
Q_LOGGING_CATEGORY(lcVk, "qt.vulkan")
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true"));
QVulkanInstance inst;
#ifndef Q_OS_ANDROID
inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
#else
inst.setLayers(QByteArrayList()
<< "VK_LAYER_GOOGLE_threading"
<< "VK_LAYER_LUNARG_parameter_validation"
<< "VK_LAYER_LUNARG_object_tracker"
<< "VK_LAYER_LUNARG_core_validation"
<< "VK_LAYER_LUNARG_image"
<< "VK_LAYER_LUNARG_swapchain"
<< "VK_LAYER_GOOGLE_unique_objects");
#endif
if (!inst.create())
qFatal("Failed to create Vulkan instance: %d", inst.errorCode());
VulkanWindow w;
w.setVulkanInstance(&inst);
if (QCoreApplication::arguments().contains(QStringLiteral("--srgb")))
w.setPreferredColorFormats(QVector<VkFormat>() << VK_FORMAT_B8G8R8A8_SRGB);
w.resize(1024, 768);
w.show();
return app.exec();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -0,0 +1,12 @@
#version 440
layout(location = 0) in vec2 v_texcoord;
layout(location = 0) out vec4 fragColor;
layout(binding = 1) uniform sampler2D tex;
void main()
{
fragColor = texture(tex, v_texcoord);
}

View File

@ -0,0 +1,18 @@
#version 440
layout(location = 0) in vec4 position;
layout(location = 1) in vec2 texcoord;
layout(location = 0) out vec2 v_texcoord;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
v_texcoord = texcoord;
gl_Position = ubuf.mvp * position;
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,12 @@
HEADERS += \
../shared/trianglerenderer.h
SOURCES += \
main.cpp \
../shared/trianglerenderer.cpp
RESOURCES += hellovulkantriangle.qrc
# install
target.path = $$[QT_INSTALL_EXAMPLES]/vulkan/hellovulkantriangle
INSTALLS += target

View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="color_vert.spv">../shared/color_vert.spv</file>
<file alias="color_frag.spv">../shared/color_frag.spv</file>
</qresource>
</RCC>

View File

@ -0,0 +1,100 @@
/****************************************************************************
**
** 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 <QGuiApplication>
#include <QVulkanInstance>
#include <QLoggingCategory>
#include "../shared/trianglerenderer.h"
Q_LOGGING_CATEGORY(lcVk, "qt.vulkan")
class VulkanWindow : public QVulkanWindow
{
public:
QVulkanWindowRenderer *createRenderer() override;
};
QVulkanWindowRenderer *VulkanWindow::createRenderer()
{
return new TriangleRenderer(this, true); // try MSAA, when available
}
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true"));
QVulkanInstance inst;
#ifndef Q_OS_ANDROID
inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
#else
inst.setLayers(QByteArrayList()
<< "VK_LAYER_GOOGLE_threading"
<< "VK_LAYER_LUNARG_parameter_validation"
<< "VK_LAYER_LUNARG_object_tracker"
<< "VK_LAYER_LUNARG_core_validation"
<< "VK_LAYER_LUNARG_image"
<< "VK_LAYER_LUNARG_swapchain"
<< "VK_LAYER_GOOGLE_unique_objects");
#endif
if (!inst.create())
qFatal("Failed to create Vulkan instance: %d", inst.errorCode());
VulkanWindow w;
w.setVulkanInstance(&inst);
w.resize(1024, 768);
w.show();
return app.exec();
}

View File

@ -0,0 +1,182 @@
/****************************************************************************
**
** 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 "hellovulkanwidget.h"
#include <QVulkanFunctions>
#include <QApplication>
#include <QVBoxLayout>
#include <QTextEdit>
#include <QPushButton>
#include <QLCDNumber>
#include <QFileDialog>
#include <QMessageBox>
MainWindow::MainWindow(VulkanWindow *w)
: m_window(w)
{
QWidget *wrapper = QWidget::createWindowContainer(w);
m_info = new QTextEdit;
m_info->setReadOnly(true);
m_number = new QLCDNumber(3);
m_number->setSegmentStyle(QLCDNumber::Filled);
QPushButton *grabButton = new QPushButton(tr("&Grab"));
grabButton->setFocusPolicy(Qt::NoFocus);
connect(grabButton, &QPushButton::clicked, this, &MainWindow::onGrabRequested);
QPushButton *quitButton = new QPushButton(tr("&Quit"));
quitButton->setFocusPolicy(Qt::NoFocus);
connect(quitButton, &QPushButton::clicked, qApp, &QCoreApplication::quit);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(m_info, 2);
layout->addWidget(m_number, 1);
layout->addWidget(wrapper, 5);
layout->addWidget(grabButton, 1);
layout->addWidget(quitButton, 1);
setLayout(layout);
}
void MainWindow::onVulkanInfoReceived(const QString &text)
{
m_info->setText(text);
}
void MainWindow::onFrameQueued(int colorValue)
{
m_number->display(colorValue);
}
void MainWindow::onGrabRequested()
{
if (!m_window->supportsGrab()) {
QMessageBox::warning(this, tr("Cannot grab"), tr("This swapchain does not support readbacks."));
return;
}
QImage img = m_window->grab();
// Our startNextFrame() implementation is synchronous so img is ready to be
// used right here.
QFileDialog fd(this);
fd.setAcceptMode(QFileDialog::AcceptSave);
fd.setDefaultSuffix("png");
fd.selectFile("test.png");
if (fd.exec() == QDialog::Accepted)
img.save(fd.selectedFiles().first());
}
QVulkanWindowRenderer *VulkanWindow::createRenderer()
{
return new VulkanRenderer(this);
}
VulkanRenderer::VulkanRenderer(VulkanWindow *w)
: TriangleRenderer(w)
{
}
void VulkanRenderer::initResources()
{
TriangleRenderer::initResources();
QVulkanInstance *inst = m_window->vulkanInstance();
m_devFuncs = inst->deviceFunctions(m_window->device());
QString info;
info += QString().sprintf("Number of physical devices: %d\n", m_window->availablePhysicalDevices().count());
QVulkanFunctions *f = inst->functions();
VkPhysicalDeviceProperties props;
f->vkGetPhysicalDeviceProperties(m_window->physicalDevice(), &props);
info += QString().sprintf("Active physical device name: '%s' version %d.%d.%d\nAPI version %d.%d.%d\n",
props.deviceName,
VK_VERSION_MAJOR(props.driverVersion), VK_VERSION_MINOR(props.driverVersion),
VK_VERSION_PATCH(props.driverVersion),
VK_VERSION_MAJOR(props.apiVersion), VK_VERSION_MINOR(props.apiVersion),
VK_VERSION_PATCH(props.apiVersion));
info += QStringLiteral("Supported instance layers:\n");
for (const QVulkanLayer &layer : inst->supportedLayers())
info += QString().sprintf(" %s v%u\n", layer.name.constData(), layer.version);
info += QStringLiteral("Enabled instance layers:\n");
for (const QByteArray &layer : inst->layers())
info += QString().sprintf(" %s\n", layer.constData());
info += QStringLiteral("Supported instance extensions:\n");
for (const QVulkanExtension &ext : inst->supportedExtensions())
info += QString().sprintf(" %s v%u\n", ext.name.constData(), ext.version);
info += QStringLiteral("Enabled instance extensions:\n");
for (const QByteArray &ext : inst->extensions())
info += QString().sprintf(" %s\n", ext.constData());
info += QString().sprintf("Color format: %u\nDepth-stencil format: %u\n",
m_window->colorFormat(), m_window->depthStencilFormat());
info += QStringLiteral("Supported sample counts:");
QList<int> sampleCounts = m_window->supportedSampleCounts().toList();
std::sort(sampleCounts.begin(), sampleCounts.end());
for (int count : sampleCounts)
info += QLatin1Char(' ') + QString::number(count);
info += QLatin1Char('\n');
emit static_cast<VulkanWindow *>(m_window)->vulkanInfoReceived(info);
}
void VulkanRenderer::startNextFrame()
{
TriangleRenderer::startNextFrame();
emit static_cast<VulkanWindow *>(m_window)->frameQueued(int(m_rotation) % 360);
}

View File

@ -0,0 +1,98 @@
/****************************************************************************
**
** 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 "../shared/trianglerenderer.h"
#include <QWidget>
class VulkanWindow;
QT_BEGIN_NAMESPACE
class QTextEdit;
class QLCDNumber;
QT_END_NAMESPACE
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow(VulkanWindow *w);
public slots:
void onVulkanInfoReceived(const QString &text);
void onFrameQueued(int colorValue);
void onGrabRequested();
private:
VulkanWindow *m_window;
QTextEdit *m_info;
QLCDNumber *m_number;
};
class VulkanRenderer : public TriangleRenderer
{
public:
VulkanRenderer(VulkanWindow *w);
void initResources() override;
void startNextFrame() override;
};
class VulkanWindow : public QVulkanWindow
{
Q_OBJECT
public:
QVulkanWindowRenderer *createRenderer() override;
signals:
void vulkanInfoReceived(const QString &text);
void frameQueued(int colorValue);
};

View File

@ -0,0 +1,16 @@
QT += widgets
HEADERS += \
hellovulkanwidget.h \
../shared/trianglerenderer.h
SOURCES += \
hellovulkanwidget.cpp \
main.cpp \
../shared/trianglerenderer.cpp
RESOURCES += hellovulkanwidget.qrc
# install
target.path = $$[QT_INSTALL_EXAMPLES]/vulkan/hellovulkanwidget
INSTALLS += target

View File

@ -0,0 +1,6 @@
<!DOCTYPE RCC><RCC version="1.0">
<qresource>
<file alias="color_vert.spv">../shared/color_vert.spv</file>
<file alias="color_frag.spv">../shared/color_frag.spv</file>
</qresource>
</RCC>

View File

@ -0,0 +1,93 @@
/****************************************************************************
**
** 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 <QApplication>
#include <QVulkanInstance>
#include <QLoggingCategory>
#include "hellovulkanwidget.h"
Q_LOGGING_CATEGORY(lcVk, "qt.vulkan")
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true"));
QVulkanInstance inst;
#ifndef Q_OS_ANDROID
inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
#else
inst.setLayers(QByteArrayList()
<< "VK_LAYER_GOOGLE_threading"
<< "VK_LAYER_LUNARG_parameter_validation"
<< "VK_LAYER_LUNARG_object_tracker"
<< "VK_LAYER_LUNARG_core_validation"
<< "VK_LAYER_LUNARG_image"
<< "VK_LAYER_LUNARG_swapchain"
<< "VK_LAYER_GOOGLE_unique_objects");
#endif
if (!inst.create())
qFatal("Failed to create Vulkan instance: %d", inst.errorCode());
VulkanWindow *vulkanWindow = new VulkanWindow;
vulkanWindow->setVulkanInstance(&inst);
MainWindow mainWindow(vulkanWindow);
QObject::connect(vulkanWindow, &VulkanWindow::vulkanInfoReceived, &mainWindow, &MainWindow::onVulkanInfoReceived);
QObject::connect(vulkanWindow, &VulkanWindow::frameQueued, &mainWindow, &MainWindow::onFrameQueued);
mainWindow.resize(1024, 768);
mainWindow.show();
return app.exec();
}

View File

@ -0,0 +1,128 @@
/****************************************************************************
**
** 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 "hellovulkanwindow.h"
#include <QVulkanFunctions>
//! [0]
QVulkanWindowRenderer *VulkanWindow::createRenderer()
{
return new VulkanRenderer(this);
}
VulkanRenderer::VulkanRenderer(QVulkanWindow *w)
: m_window(w)
{
}
//! [0]
//! [1]
void VulkanRenderer::initResources()
{
qDebug("initResources");
m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device());
}
//! [1]
void VulkanRenderer::initSwapChainResources()
{
qDebug("initSwapChainResources");
}
void VulkanRenderer::releaseSwapChainResources()
{
qDebug("releaseSwapChainResources");
}
void VulkanRenderer::releaseResources()
{
qDebug("releaseResources");
}
//! [2]
void VulkanRenderer::startNextFrame()
{
m_green += 0.005f;
if (m_green > 1.0f)
m_green = 0.0f;
VkClearColorValue clearColor = { 0.0f, m_green, 0.0f, 1.0f };
VkClearDepthStencilValue clearDS = { 1.0f, 0 };
VkClearValue clearValues[2];
memset(clearValues, 0, sizeof(clearValues));
clearValues[0].color = clearColor;
clearValues[1].depthStencil = clearDS;
VkRenderPassBeginInfo rpBeginInfo;
memset(&rpBeginInfo, 0, sizeof(rpBeginInfo));
rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rpBeginInfo.renderPass = m_window->defaultRenderPass();
rpBeginInfo.framebuffer = m_window->currentFramebuffer();
const QSize sz = m_window->swapChainImageSize();
rpBeginInfo.renderArea.extent.width = sz.width();
rpBeginInfo.renderArea.extent.height = sz.height();
rpBeginInfo.clearValueCount = 2;
rpBeginInfo.pClearValues = clearValues;
VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
// Do nothing else. We will just clear to green, changing the component on
// every invocation. This also helps verifying the rate to which the thread
// is throttled to. (The elapsed time between startNextFrame calls should
// typically be around 16 ms. Note that rendering is 2 frames ahead of what
// is displayed.)
m_devFuncs->vkCmdEndRenderPass(cmdBuf);
m_window->frameReady();
m_window->requestUpdate(); // render continuously, throttled by the presentation rate
}
//! [2]

View File

@ -0,0 +1,77 @@
/****************************************************************************
**
** 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 <QVulkanWindow>
//! [0]
class VulkanRenderer : public QVulkanWindowRenderer
{
public:
VulkanRenderer(QVulkanWindow *w);
void initResources() override;
void initSwapChainResources() override;
void releaseSwapChainResources() override;
void releaseResources() override;
void startNextFrame() override;
private:
QVulkanWindow *m_window;
QVulkanDeviceFunctions *m_devFuncs;
float m_green = 0;
};
class VulkanWindow : public QVulkanWindow
{
public:
QVulkanWindowRenderer *createRenderer() override;
};
//! [0]

View File

@ -0,0 +1,6 @@
HEADERS += hellovulkanwindow.h
SOURCES += hellovulkanwindow.cpp main.cpp
# install
target.path = $$[QT_INSTALL_EXAMPLES]/vulkan/hellovulkanwindow
INSTALLS += target

View File

@ -0,0 +1,93 @@
/****************************************************************************
**
** 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 <QGuiApplication>
#include <QVulkanInstance>
#include <QLoggingCategory>
#include "hellovulkanwindow.h"
Q_LOGGING_CATEGORY(lcVk, "qt.vulkan")
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true"));
//! [0]
QVulkanInstance inst;
#ifndef Q_OS_ANDROID
inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
#else
inst.setLayers(QByteArrayList()
<< "VK_LAYER_GOOGLE_threading"
<< "VK_LAYER_LUNARG_parameter_validation"
<< "VK_LAYER_LUNARG_object_tracker"
<< "VK_LAYER_LUNARG_core_validation"
<< "VK_LAYER_LUNARG_image"
<< "VK_LAYER_LUNARG_swapchain"
<< "VK_LAYER_GOOGLE_unique_objects");
#endif
if (!inst.create())
qFatal("Failed to create Vulkan instance: %d", inst.errorCode());
//! [0]
//! [1]
VulkanWindow w;
w.setVulkanInstance(&inst);
w.resize(1024, 768);
w.show();
//! [1]
return app.exec();
}

View File

@ -0,0 +1,10 @@
#version 440
layout(location = 0) in vec3 v_color;
layout(location = 0) out vec4 fragColor;
void main()
{
fragColor = vec4(v_color, 1.0);
}

View File

@ -0,0 +1,18 @@
#version 440
layout(location = 0) in vec4 position;
layout(location = 1) in vec3 color;
layout(location = 0) out vec3 v_color;
layout(std140, binding = 0) uniform buf {
mat4 mvp;
} ubuf;
out gl_PerVertex { vec4 gl_Position; };
void main()
{
v_color = color;
gl_Position = ubuf.mvp * position;
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,513 @@
/****************************************************************************
**
** 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 "trianglerenderer.h"
#include <QVulkanFunctions>
#include <QFile>
// 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[] = {
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f
};
static const int UNIFORM_DATA_SIZE = 16 * sizeof(float);
static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
{
return (v + byteAlign - 1) & ~(byteAlign - 1);
}
TriangleRenderer::TriangleRenderer(QVulkanWindow *w, bool msaa)
: m_window(w)
{
if (msaa) {
QSet<int> counts = w->supportedSampleCounts();
qDebug() << "Supported sample counts:" << counts;
for (int s = 16; s >= 4; s /= 2) {
if (counts.contains(s)) {
qDebug("Requesting sample count %d", s);
m_window->setSampleCount(s);
break;
}
}
}
}
VkShaderModule TriangleRenderer::createShader(const QString &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(&shaderInfo, 0, sizeof(shaderInfo));
shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shaderInfo.codeSize = blob.size();
shaderInfo.pCode = reinterpret_cast<const uint32_t *>(blob.constData());
VkShaderModule shaderModule;
VkResult err = m_devFuncs->vkCreateShaderModule(m_window->device(), &shaderInfo, nullptr, &shaderModule);
if (err != VK_SUCCESS) {
qWarning("Failed to create shader module: %d", err);
return VK_NULL_HANDLE;
}
return shaderModule;
}
void TriangleRenderer::initResources()
{
qDebug("initResources");
VkDevice dev = m_window->device();
m_devFuncs = m_window->vulkanInstance()->deviceFunctions(dev);
// Prepare the vertex and uniform data. The vertex data will never
// change so one buffer is sufficient regardless of the value of
// QVulkanWindow::CONCURRENT_FRAME_COUNT. Uniform data is changing per
// frame however so active frames have to have a dedicated copy.
// Use just one memory allocation and one buffer. We will then specify the
// appropriate offsets for uniform buffers in the VkDescriptorBufferInfo.
// Have to watch out for
// VkPhysicalDeviceLimits::minUniformBufferOffsetAlignment, though.
// The uniform buffer is not strictly required in this example, we could
// have used push constants as well since our single matrix (64 bytes) fits
// into the spec mandated minimum limit of 128 bytes. However, once that
// limit is not sufficient, the per-frame buffers, as shown below, will
// become necessary.
const int concurrentFrameCount = m_window->concurrentFrameCount();
const VkPhysicalDeviceLimits *pdevLimits = &m_window->physicalDeviceProperties()->limits;
const VkDeviceSize uniAlign = pdevLimits->minUniformBufferOffsetAlignment;
qDebug("uniform buffer offset alignment is %u", (uint) uniAlign);
VkBufferCreateInfo bufInfo;
memset(&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->vkCreateBuffer(dev, &bufInfo, nullptr, &m_buf);
if (err != VK_SUCCESS)
qFatal("Failed to create buffer: %d", err);
VkMemoryRequirements memReq;
m_devFuncs->vkGetBufferMemoryRequirements(dev, m_buf, &memReq);
VkMemoryAllocateInfo memAllocInfo = {
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
nullptr,
memReq.size,
m_window->hostVisibleMemoryIndex()
};
err = m_devFuncs->vkAllocateMemory(dev, &memAllocInfo, nullptr, &m_bufMem);
if (err != VK_SUCCESS)
qFatal("Failed to allocate memory: %d", err);
err = m_devFuncs->vkBindBufferMemory(dev, m_buf, m_bufMem, 0);
if (err != VK_SUCCESS)
qFatal("Failed to bind buffer memory: %d", err);
quint8 *p;
err = m_devFuncs->vkMapMemory(dev, m_bufMem, 0, memReq.size, 0, reinterpret_cast<void **>(&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 < 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->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_R32G32_SFLOAT,
0
},
{ // color
1,
0,
VK_FORMAT_R32G32B32_SFLOAT,
2 * 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 = &vertexBindingDesc;
vertexInputInfo.vertexAttributeDescriptionCount = 2;
vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc;
// Set up descriptor set and its layout.
VkDescriptorPoolSize descPoolSizes = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uint32_t(concurrentFrameCount) };
VkDescriptorPoolCreateInfo descPoolInfo;
memset(&descPoolInfo, 0, sizeof(descPoolInfo));
descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descPoolInfo.maxSets = concurrentFrameCount;
descPoolInfo.poolSizeCount = 1;
descPoolInfo.pPoolSizes = &descPoolSizes;
err = m_devFuncs->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, &m_descPool);
if (err != VK_SUCCESS)
qFatal("Failed to create descriptor pool: %d", err);
VkDescriptorSetLayoutBinding layoutBinding = {
0, // binding
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
1,
VK_SHADER_STAGE_VERTEX_BIT,
nullptr
};
VkDescriptorSetLayoutCreateInfo descLayoutInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
nullptr,
0,
1,
&layoutBinding
};
err = m_devFuncs->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_descSetLayout);
if (err != VK_SUCCESS)
qFatal("Failed to create descriptor set layout: %d", err);
for (int i = 0; i < concurrentFrameCount; ++i) {
VkDescriptorSetAllocateInfo descSetAllocInfo = {
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
nullptr,
m_descPool,
1,
&m_descSetLayout
};
err = m_devFuncs->vkAllocateDescriptorSets(dev, &descSetAllocInfo, &m_descSet[i]);
if (err != VK_SUCCESS)
qFatal("Failed to allocate descriptor set: %d", err);
VkWriteDescriptorSet descWrite;
memset(&descWrite, 0, sizeof(descWrite));
descWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descWrite.dstSet = m_descSet[i];
descWrite.descriptorCount = 1;
descWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
descWrite.pBufferInfo = &m_uniformBufInfo[i];
m_devFuncs->vkUpdateDescriptorSets(dev, 1, &descWrite, 0, nullptr);
}
// Pipeline cache
VkPipelineCacheCreateInfo pipelineCacheInfo;
memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo));
pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
err = m_devFuncs->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &m_pipelineCache);
if (err != VK_SUCCESS)
qFatal("Failed to create pipeline cache: %d", err);
// Pipeline layout
VkPipelineLayoutCreateInfo pipelineLayoutInfo;
memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &m_descSetLayout;
err = m_devFuncs->vkCreatePipelineLayout(dev, &pipelineLayoutInfo, nullptr, &m_pipelineLayout);
if (err != VK_SUCCESS)
qFatal("Failed to create pipeline layout: %d", err);
// Shaders
VkShaderModule vertShaderModule = createShader(QStringLiteral(":/color_vert.spv"));
VkShaderModule fragShaderModule = createShader(QStringLiteral(":/color_frag.spv"));
// Graphics pipeline
VkGraphicsPipelineCreateInfo pipelineInfo;
memset(&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 = &vertexInputInfo;
VkPipelineInputAssemblyStateCreateInfo ia;
memset(&ia, 0, sizeof(ia));
ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
pipelineInfo.pInputAssemblyState = &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(&vp, 0, sizeof(vp));
vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
vp.viewportCount = 1;
vp.scissorCount = 1;
pipelineInfo.pViewportState = &vp;
VkPipelineRasterizationStateCreateInfo rs;
memset(&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_NONE; // we want the back face as well
rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rs.lineWidth = 1.0f;
pipelineInfo.pRasterizationState = &rs;
VkPipelineMultisampleStateCreateInfo ms;
memset(&ms, 0, sizeof(ms));
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
// Enable multisampling.
ms.rasterizationSamples = m_window->sampleCountFlagBits();
pipelineInfo.pMultisampleState = &ms;
VkPipelineDepthStencilStateCreateInfo ds;
memset(&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 = &ds;
VkPipelineColorBlendStateCreateInfo cb;
memset(&cb, 0, sizeof(cb));
cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
// no blend, write out all of rgba
VkPipelineColorBlendAttachmentState att;
memset(&att, 0, sizeof(att));
att.colorWriteMask = 0xF;
cb.attachmentCount = 1;
cb.pAttachments = &att;
pipelineInfo.pColorBlendState = &cb;
VkDynamicState dynEnable[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VkPipelineDynamicStateCreateInfo dyn;
memset(&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 = &dyn;
pipelineInfo.layout = m_pipelineLayout;
pipelineInfo.renderPass = m_window->defaultRenderPass();
err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_pipeline);
if (err != VK_SUCCESS)
qFatal("Failed to create graphics pipeline: %d", err);
if (vertShaderModule)
m_devFuncs->vkDestroyShaderModule(dev, vertShaderModule, nullptr);
if (fragShaderModule)
m_devFuncs->vkDestroyShaderModule(dev, fragShaderModule, nullptr);
}
void TriangleRenderer::initSwapChainResources()
{
qDebug("initSwapChainResources");
// Projection matrix
m_proj = *m_window->clipCorrectionMatrix(); // adjust for Vulkan-OpenGL clip space differences
const QSize sz = m_window->swapChainImageSize();
m_proj.perspective(45.0f, sz.width() / (float) sz.height(), 0.01f, 100.0f);
m_proj.translate(0, 0, -4);
}
void TriangleRenderer::releaseSwapChainResources()
{
qDebug("releaseSwapChainResources");
}
void TriangleRenderer::releaseResources()
{
qDebug("releaseResources");
VkDevice dev = m_window->device();
if (m_pipeline) {
m_devFuncs->vkDestroyPipeline(dev, m_pipeline, nullptr);
m_pipeline = VK_NULL_HANDLE;
}
if (m_pipelineLayout) {
m_devFuncs->vkDestroyPipelineLayout(dev, m_pipelineLayout, nullptr);
m_pipelineLayout = VK_NULL_HANDLE;
}
if (m_pipelineCache) {
m_devFuncs->vkDestroyPipelineCache(dev, m_pipelineCache, nullptr);
m_pipelineCache = VK_NULL_HANDLE;
}
if (m_descSetLayout) {
m_devFuncs->vkDestroyDescriptorSetLayout(dev, m_descSetLayout, nullptr);
m_descSetLayout = VK_NULL_HANDLE;
}
if (m_descPool) {
m_devFuncs->vkDestroyDescriptorPool(dev, m_descPool, nullptr);
m_descPool = VK_NULL_HANDLE;
}
if (m_buf) {
m_devFuncs->vkDestroyBuffer(dev, m_buf, nullptr);
m_buf = VK_NULL_HANDLE;
}
if (m_bufMem) {
m_devFuncs->vkFreeMemory(dev, m_bufMem, nullptr);
m_bufMem = VK_NULL_HANDLE;
}
}
void TriangleRenderer::startNextFrame()
{
VkDevice dev = m_window->device();
VkCommandBuffer cb = m_window->currentCommandBuffer();
const QSize sz = m_window->swapChainImageSize();
VkClearColorValue clearColor = { 0, 0, 0, 1 };
VkClearDepthStencilValue clearDS = { 1, 0 };
VkClearValue clearValues[3];
memset(clearValues, 0, sizeof(clearValues));
clearValues[0].color = clearValues[2].color = clearColor;
clearValues[1].depthStencil = clearDS;
VkRenderPassBeginInfo rpBeginInfo;
memset(&rpBeginInfo, 0, sizeof(rpBeginInfo));
rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rpBeginInfo.renderPass = m_window->defaultRenderPass();
rpBeginInfo.framebuffer = m_window->currentFramebuffer();
rpBeginInfo.renderArea.extent.width = sz.width();
rpBeginInfo.renderArea.extent.height = sz.height();
rpBeginInfo.clearValueCount = m_window->sampleCountFlagBits() > VK_SAMPLE_COUNT_1_BIT ? 3 : 2;
rpBeginInfo.pClearValues = clearValues;
VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
quint8 *p;
VkResult err = m_devFuncs->vkMapMemory(dev, m_bufMem, m_uniformBufInfo[m_window->currentFrame()].offset,
UNIFORM_DATA_SIZE, 0, reinterpret_cast<void **>(&p));
if (err != VK_SUCCESS)
qFatal("Failed to map memory: %d", err);
QMatrix4x4 m = m_proj;
m.rotate(m_rotation, 0, 1, 0);
memcpy(p, m.constData(), 16 * sizeof(float));
m_devFuncs->vkUnmapMemory(dev, m_bufMem);
// Not exactly a real animation system, just advance on every frame for now.
m_rotation += 1.0f;
m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1,
&m_descSet[m_window->currentFrame()], 0, nullptr);
VkDeviceSize vbOffset = 0;
m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_buf, &vbOffset);
VkViewport viewport;
viewport.x = viewport.y = 0;
viewport.width = sz.width();
viewport.height = sz.height();
viewport.minDepth = 0;
viewport.maxDepth = 1;
m_devFuncs->vkCmdSetViewport(cb, 0, 1, &viewport);
VkRect2D scissor;
scissor.offset.x = scissor.offset.y = 0;
scissor.extent.width = viewport.width;
scissor.extent.height = viewport.height;
m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor);
m_devFuncs->vkCmdDraw(cb, 3, 1, 0, 0);
m_devFuncs->vkCmdEndRenderPass(cmdBuf);
m_window->frameReady();
m_window->requestUpdate(); // render continuously, throttled by the presentation rate
}

View File

@ -0,0 +1,85 @@
/****************************************************************************
**
** 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 <QVulkanWindow>
class TriangleRenderer : public QVulkanWindowRenderer
{
public:
TriangleRenderer(QVulkanWindow *w, bool msaa = false);
void initResources() override;
void initSwapChainResources() override;
void releaseSwapChainResources() override;
void releaseResources() override;
void startNextFrame() override;
protected:
VkShaderModule createShader(const QString &name);
QVulkanWindow *m_window;
QVulkanDeviceFunctions *m_devFuncs;
VkDeviceMemory m_bufMem = VK_NULL_HANDLE;
VkBuffer m_buf = VK_NULL_HANDLE;
VkDescriptorBufferInfo m_uniformBufInfo[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
VkDescriptorPool m_descPool = VK_NULL_HANDLE;
VkDescriptorSetLayout m_descSetLayout = VK_NULL_HANDLE;
VkDescriptorSet m_descSet[QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT];
VkPipelineCache m_pipelineCache = VK_NULL_HANDLE;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
VkPipeline m_pipeline = VK_NULL_HANDLE;
QMatrix4x4 m_proj;
float m_rotation = 0.0f;
};

View File

@ -0,0 +1,7 @@
TEMPLATE = subdirs
SUBDIRS = hellovulkanwindow \
hellovulkantriangle \
hellovulkantexture
qtHaveModule(widgets): SUBDIRS += hellovulkanwidget

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,161 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QVULKANWINDOW_H
#define QVULKANWINDOW_H
#include <QtGui/qtguiglobal.h>
#if QT_CONFIG(vulkan)
#include <QtGui/qvulkaninstance.h>
#include <QtGui/qwindow.h>
#include <QtGui/qimage.h>
#include <QtGui/qmatrix4x4.h>
#include <QtCore/qset.h>
QT_BEGIN_NAMESPACE
class QVulkanWindowPrivate;
class Q_GUI_EXPORT QVulkanWindowRenderer
{
public:
virtual ~QVulkanWindowRenderer();
virtual void preInitResources();
virtual void initResources();
virtual void initSwapChainResources();
virtual void releaseSwapChainResources();
virtual void releaseResources();
virtual void startNextFrame() = 0;
virtual void physicalDeviceLost();
virtual void logicalDeviceLost();
};
class Q_GUI_EXPORT QVulkanWindow : public QWindow
{
Q_OBJECT
Q_DECLARE_PRIVATE(QVulkanWindow)
public:
enum Flag {
PersistentResources = 0x01
};
Q_DECLARE_FLAGS(Flags, Flag)
explicit QVulkanWindow(QWindow *parent = nullptr);
~QVulkanWindow();
void setFlags(Flags flags);
Flags flags() const;
QVector<VkPhysicalDeviceProperties> availablePhysicalDevices();
void setPhysicalDeviceIndex(int idx);
QVulkanInfoVector<QVulkanExtension> supportedDeviceExtensions();
void setDeviceExtensions(const QByteArrayList &extensions);
void setPreferredColorFormats(const QVector<VkFormat> &formats);
QSet<int> supportedSampleCounts();
void setSampleCount(int sampleCount);
bool isValid() const;
virtual QVulkanWindowRenderer *createRenderer();
void frameReady();
VkPhysicalDevice physicalDevice() const;
const VkPhysicalDeviceProperties *physicalDeviceProperties() const;
VkDevice device() const;
VkQueue graphicsQueue() const;
VkCommandPool graphicsCommandPool() const;
uint32_t hostVisibleMemoryIndex() const;
uint32_t deviceLocalMemoryIndex() const;
VkRenderPass defaultRenderPass() const;
VkFormat colorFormat() const;
VkFormat depthStencilFormat() const;
QSize swapChainImageSize() const;
VkCommandBuffer currentCommandBuffer() const;
VkFramebuffer currentFramebuffer() const;
int currentFrame() const;
static const int MAX_CONCURRENT_FRAME_COUNT = 3;
int concurrentFrameCount() const;
int swapChainImageCount() const;
int currentSwapChainImageIndex() const;
VkImage swapChainImage(int idx) const;
VkImageView swapChainImageView(int idx) const;
VkImage depthStencilImage() const;
VkImageView depthStencilImageView() const;
VkSampleCountFlagBits sampleCountFlagBits() const;
VkImage msaaColorImage(int idx) const;
VkImageView msaaColorImageView(int idx) const;
bool supportsGrab() const;
QImage grab();
const QMatrix4x4 *clipCorrectionMatrix();
Q_SIGNALS:
void frameGrabbed(const QImage &image);
protected:
void exposeEvent(QExposeEvent *) override;
void resizeEvent(QResizeEvent *) override;
bool event(QEvent *) override;
private:
Q_DISABLE_COPY(QVulkanWindow)
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QVulkanWindow::Flags)
QT_END_NAMESPACE
#endif // QT_CONFIG(vulkan)
#endif

View File

@ -0,0 +1,188 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QVULKANWINDOW_P_H
#define QVULKANWINDOW_P_H
#include <QtGui/private/qtguiglobal_p.h>
#if QT_CONFIG(vulkan)
#include "qvulkanwindow.h"
#include <QtCore/QHash>
#include <private/qwindow_p.h>
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of a number of Qt sources files. This header file may change from
// version to version without notice, or even be removed.
//
// We mean it.
//
QT_BEGIN_NAMESPACE
class QVulkanWindowPrivate : public QWindowPrivate
{
Q_DECLARE_PUBLIC(QVulkanWindow)
public:
~QVulkanWindowPrivate();
void ensureStarted();
void init();
void reset();
bool createDefaultRenderPass();
void recreateSwapChain();
uint32_t chooseTransientImageMemType(VkImage img, uint32_t startIndex);
bool createTransientImage(VkFormat format, VkImageUsageFlags usage, VkImageAspectFlags aspectMask,
VkImage *images, VkDeviceMemory *mem, VkImageView *views, int count);
void releaseSwapChain();
void beginFrame();
void endFrame();
bool checkDeviceLost(VkResult err);
void addReadback();
void finishBlockingReadback();
enum Status {
StatusUninitialized,
StatusFail,
StatusFailRetry,
StatusDeviceReady,
StatusReady
};
Status status = StatusUninitialized;
QVulkanWindowRenderer *renderer = nullptr;
QVulkanInstance *inst = nullptr;
VkSurfaceKHR surface = VK_NULL_HANDLE;
int physDevIndex = 0;
QVector<VkPhysicalDevice> physDevs;
QVector<VkPhysicalDeviceProperties> physDevProps;
QVulkanWindow::Flags flags = 0;
QByteArrayList requestedDevExtensions;
QHash<VkPhysicalDevice, QVulkanInfoVector<QVulkanExtension> > supportedDevExtensions;
QVector<VkFormat> requestedColorFormats;
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;
VkDevice dev = VK_NULL_HANDLE;
QVulkanDeviceFunctions *devFuncs;
uint32_t gfxQueueFamilyIdx;
uint32_t presQueueFamilyIdx;
VkQueue gfxQueue;
VkQueue presQueue;
VkCommandPool cmdPool = VK_NULL_HANDLE;
VkCommandPool presCmdPool = VK_NULL_HANDLE;
uint32_t hostVisibleMemIndex;
uint32_t deviceLocalMemIndex;
VkFormat colorFormat;
VkColorSpaceKHR colorSpace;
VkFormat dsFormat = VK_FORMAT_D24_UNORM_S8_UINT;
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR = nullptr;
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR;
PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR;
PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR;
PFN_vkQueuePresentKHR vkQueuePresentKHR;
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vkGetPhysicalDeviceSurfaceCapabilitiesKHR = nullptr;
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vkGetPhysicalDeviceSurfaceFormatsKHR;
static const int MAX_SWAPCHAIN_BUFFER_COUNT = 3;
static const int MAX_FRAME_LAG = QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT;
// QVulkanWindow only supports the always available FIFO mode. The
// rendering thread will get throttled to the presentation rate (vsync).
// This is in effect Example 5 from the VK_KHR_swapchain spec.
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
int swapChainBufferCount = 2;
int frameLag = 2;
QSize swapChainImageSize;
VkSwapchainKHR swapChain = VK_NULL_HANDLE;
bool swapChainSupportsReadBack = false;
struct ImageResources {
VkImage image = VK_NULL_HANDLE;
VkImageView imageView = VK_NULL_HANDLE;
VkCommandBuffer cmdBuf = VK_NULL_HANDLE;
VkFence cmdFence = VK_NULL_HANDLE;
bool cmdFenceWaitable = false;
VkFramebuffer fb = VK_NULL_HANDLE;
VkCommandBuffer presTransCmdBuf = VK_NULL_HANDLE;
VkImage msaaImage = VK_NULL_HANDLE;
VkImageView msaaImageView = VK_NULL_HANDLE;
} imageRes[MAX_SWAPCHAIN_BUFFER_COUNT];
VkDeviceMemory msaaImageMem = VK_NULL_HANDLE;
uint32_t currentImage;
struct FrameResources {
VkFence fence = VK_NULL_HANDLE;
bool fenceWaitable = false;
VkSemaphore imageSem = VK_NULL_HANDLE;
VkSemaphore drawSem = VK_NULL_HANDLE;
VkSemaphore presTransSem = VK_NULL_HANDLE;
bool imageAcquired = false;
bool imageSemWaitable = false;
} frameRes[MAX_FRAME_LAG];
uint32_t currentFrame;
VkRenderPass defaultRenderPass = VK_NULL_HANDLE;
VkDeviceMemory dsMem = VK_NULL_HANDLE;
VkImage dsImage = VK_NULL_HANDLE;
VkImageView dsView = VK_NULL_HANDLE;
bool framePending = false;
bool frameGrabbing = false;
QImage frameGrabTargetImage;
VkImage frameGrabImage = VK_NULL_HANDLE;
VkDeviceMemory frameGrabImageMem = VK_NULL_HANDLE;
QMatrix4x4 m_clipCorrect;
};
QT_END_NAMESPACE
#endif // QT_CONFIG(vulkan)
#endif

View File

@ -1,12 +1,15 @@
qtConfig(vulkan) {
HEADERS += \
vulkan/qvulkaninstance.h \
vulkan/qplatformvulkaninstance.h
vulkan/qplatformvulkaninstance.h \
vulkan/qvulkanwindow.h \
vulkan/qvulkanwindow_p.h
SOURCES += \
vulkan/qvulkaninstance.cpp \
vulkan/qplatformvulkaninstance.cpp \
vulkan/qvulkanfunctions.cpp
vulkan/qvulkanfunctions.cpp \
vulkan/qvulkanwindow.cpp
# Applications must inherit the Vulkan header include path.
QMAKE_USE += vulkan/nolink

View File

@ -28,7 +28,7 @@
#include <QtGui/QVulkanInstance>
#include <QtGui/QVulkanFunctions>
#include <QtGui/QWindow>
#include <QtGui/QVulkanWindow>
#include <QtTest/QtTest>
@ -41,8 +41,11 @@ class tst_QVulkan : public QObject
private slots:
void vulkanInstance();
void vulkanCheckSupported();
void vulkanWindow();
void vulkanPlainWindow();
void vulkanVersionRequest();
void vulkanWindow();
void vulkanWindowRenderer();
void vulkanWindowGrab();
};
void tst_QVulkan::vulkanInstance()
@ -102,7 +105,7 @@ void tst_QVulkan::vulkanCheckSupported()
}
}
void tst_QVulkan::vulkanWindow()
void tst_QVulkan::vulkanPlainWindow()
{
QVulkanInstance inst;
if (!inst.create())
@ -154,6 +157,279 @@ void tst_QVulkan::vulkanVersionRequest()
QCOMPARE(inst.errorCode(), VK_ERROR_INCOMPATIBLE_DRIVER);
}
static void waitForUnexposed(QWindow *w)
{
QElapsedTimer timer;
timer.start();
while (w->isExposed()) {
int remaining = 5000 - int(timer.elapsed());
if (remaining <= 0)
break;
QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
QCoreApplication::sendPostedEvents(Q_NULLPTR, QEvent::DeferredDelete);
QTest::qSleep(10);
}
}
void tst_QVulkan::vulkanWindow()
{
QVulkanInstance inst;
if (!inst.create())
QSKIP("Vulkan init failed; skip");
// First let's forget to set the instance.
QVulkanWindow w;
QVERIFY(!w.isValid());
w.resize(1024, 768);
w.show();
QTest::qWaitForWindowExposed(&w);
QVERIFY(!w.isValid());
// Now set it. A simple hide - show should be enough to correct, this, no
// need for a full destroy - create.
w.hide();
waitForUnexposed(&w);
w.setVulkanInstance(&inst);
QVector<VkPhysicalDeviceProperties> pdevs = w.availablePhysicalDevices();
if (pdevs.isEmpty())
QSKIP("No Vulkan physical devices; skip");
w.show();
QTest::qWaitForWindowExposed(&w);
QVERIFY(w.isValid());
QCOMPARE(w.vulkanInstance(), &inst);
QVulkanInfoVector<QVulkanExtension> exts = w.supportedDeviceExtensions();
// Now destroy and recreate.
w.destroy();
waitForUnexposed(&w);
QVERIFY(!w.isValid());
// check that flags can be set between a destroy() - show()
w.setFlags(QVulkanWindow::PersistentResources);
// supported lists can be queried before expose too
QVERIFY(w.supportedDeviceExtensions() == exts);
w.show();
QTest::qWaitForWindowExposed(&w);
QVERIFY(w.isValid());
QVERIFY(w.flags().testFlag(QVulkanWindow::PersistentResources));
QVERIFY(w.physicalDevice() != VK_NULL_HANDLE);
QVERIFY(w.physicalDeviceProperties() != nullptr);
QVERIFY(w.device() != VK_NULL_HANDLE);
QVERIFY(w.graphicsQueue() != VK_NULL_HANDLE);
QVERIFY(w.graphicsCommandPool() != VK_NULL_HANDLE);
QVERIFY(w.defaultRenderPass() != VK_NULL_HANDLE);
QVERIFY(w.concurrentFrameCount() > 0);
QVERIFY(w.concurrentFrameCount() <= QVulkanWindow::MAX_CONCURRENT_FRAME_COUNT);
}
class TestVulkanRenderer;
class TestVulkanWindow : public QVulkanWindow
{
public:
QVulkanWindowRenderer *createRenderer() override;
private:
TestVulkanRenderer *m_renderer = nullptr;
};
struct TestVulkan {
int preInitResCount = 0;
int initResCount = 0;
int initSwcResCount = 0;
int releaseResCount = 0;
int releaseSwcResCount = 0;
int startNextFrameCount = 0;
} testVulkan;
class TestVulkanRenderer : public QVulkanWindowRenderer
{
public:
TestVulkanRenderer(QVulkanWindow *w) : m_window(w) { }
void preInitResources() override;
void initResources() override;
void initSwapChainResources() override;
void releaseSwapChainResources() override;
void releaseResources() override;
void startNextFrame() override;
private:
QVulkanWindow *m_window;
QVulkanDeviceFunctions *m_devFuncs;
};
void TestVulkanRenderer::preInitResources()
{
if (testVulkan.initResCount) {
qWarning("initResources called before preInitResources?!");
testVulkan.preInitResCount = -1;
return;
}
// Ensure the physical device and the surface are available at this stage.
VkPhysicalDevice physDev = m_window->physicalDevice();
if (physDev == VK_NULL_HANDLE) {
qWarning("No physical device in preInitResources");
testVulkan.preInitResCount = -1;
return;
}
VkSurfaceKHR surface = m_window->vulkanInstance()->surfaceForWindow(m_window);
if (surface == VK_NULL_HANDLE) {
qWarning("No surface in preInitResources");
testVulkan.preInitResCount = -1;
return;
}
++testVulkan.preInitResCount;
}
void TestVulkanRenderer::initResources()
{
m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device());
++testVulkan.initResCount;
}
void TestVulkanRenderer::initSwapChainResources()
{
++testVulkan.initSwcResCount;
}
void TestVulkanRenderer::releaseSwapChainResources()
{
++testVulkan.releaseSwcResCount;
}
void TestVulkanRenderer::releaseResources()
{
++testVulkan.releaseResCount;
}
void TestVulkanRenderer::startNextFrame()
{
++testVulkan.startNextFrameCount;
VkClearColorValue clearColor = { 0, 1, 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(&rpBeginInfo, 0, sizeof(rpBeginInfo));
rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
rpBeginInfo.renderPass = m_window->defaultRenderPass();
rpBeginInfo.framebuffer = m_window->currentFramebuffer();
const QSize sz = m_window->swapChainImageSize();
rpBeginInfo.renderArea.extent.width = sz.width();
rpBeginInfo.renderArea.extent.height = sz.height();
rpBeginInfo.clearValueCount = 2;
rpBeginInfo.pClearValues = clearValues;
VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
m_devFuncs->vkCmdEndRenderPass(cmdBuf);
m_window->frameReady();
}
QVulkanWindowRenderer *TestVulkanWindow::createRenderer()
{
Q_ASSERT(!m_renderer);
m_renderer = new TestVulkanRenderer(this);
return m_renderer;
}
void tst_QVulkan::vulkanWindowRenderer()
{
QVulkanInstance inst;
if (!inst.create())
QSKIP("Vulkan init failed; skip");
testVulkan = TestVulkan();
TestVulkanWindow w;
w.setVulkanInstance(&inst);
w.resize(1024, 768);
w.show();
QTest::qWaitForWindowExposed(&w);
if (w.availablePhysicalDevices().isEmpty())
QSKIP("No Vulkan physical devices; skip");
QVERIFY(testVulkan.preInitResCount == 1);
QVERIFY(testVulkan.initResCount == 1);
QVERIFY(testVulkan.initSwcResCount == 1);
// this has to be QTRY due to the async update in QVulkanWindowPrivate::ensureStarted()
QTRY_VERIFY(testVulkan.startNextFrameCount >= 1);
QVERIFY(!w.swapChainImageSize().isEmpty());
QVERIFY(w.colorFormat() != VK_FORMAT_UNDEFINED);
QVERIFY(w.depthStencilFormat() != VK_FORMAT_UNDEFINED);
w.destroy();
waitForUnexposed(&w);
QVERIFY(testVulkan.releaseSwcResCount == 1);
QVERIFY(testVulkan.releaseResCount == 1);
}
void tst_QVulkan::vulkanWindowGrab()
{
QVulkanInstance inst;
inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
if (!inst.create())
QSKIP("Vulkan init failed; skip");
testVulkan = TestVulkan();
TestVulkanWindow w;
w.setVulkanInstance(&inst);
w.resize(1024, 768);
w.show();
QTest::qWaitForWindowExposed(&w);
if (w.availablePhysicalDevices().isEmpty())
QSKIP("No Vulkan physical devices; skip");
if (!w.supportsGrab())
QSKIP("No grab support; skip");
QVERIFY(!w.swapChainImageSize().isEmpty());
QImage img1 = w.grab();
QImage img2 = w.grab();
QImage img3 = w.grab();
QVERIFY(!img1.isNull());
QVERIFY(!img2.isNull());
QVERIFY(!img3.isNull());
QCOMPARE(img1.size(), w.swapChainImageSize());
QCOMPARE(img2.size(), w.swapChainImageSize());
QCOMPARE(img3.size(), w.swapChainImageSize());
QRgb a = img1.pixel(10, 20);
QRgb b = img2.pixel(5, 5);
QRgb c = img3.pixel(50, 30);
QCOMPARE(a, b);
QCOMPARE(b, c);
QRgb refPixel = qRgb(0, 255, 0);
int redFuzz = qAbs(qRed(a) - qRed(refPixel));
int greenFuzz = qAbs(qGreen(a) - qGreen(refPixel));
int blueFuzz = qAbs(qBlue(a) - qBlue(refPixel));
QVERIFY(redFuzz <= 1);
QVERIFY(blueFuzz <= 1);
QVERIFY(greenFuzz <= 1);
w.destroy();
}
QTEST_MAIN(tst_QVulkan)
#include "tst_qvulkan.moc"