summaryrefslogtreecommitdiffstats
path: root/examples/vulkan/hellovulkancubes
diff options
context:
space:
mode:
authorLaszlo Agocs <laszlo.agocs@qt.io>2017-03-24 14:54:24 +0100
committerLaszlo Agocs <laszlo.agocs@qt.io>2017-03-29 10:58:06 +0000
commitc72b821b5911842757a3929fb287471ad5d457ea (patch)
tree07aace2498733596acc411cf096fcff998485c42 /examples/vulkan/hellovulkancubes
parente7a7931ba1e4fd6d80674c73e6e9e9a8099b9391 (diff)
Add the Vulkan cube example
Features a little bit more than a cube, though. Change-Id: I636d4875ba9ccf722ed3caab97f14570be785748 Reviewed-by: Andy Nichols <andy.nichols@qt.io>
Diffstat (limited to 'examples/vulkan/hellovulkancubes')
-rw-r--r--examples/vulkan/hellovulkancubes/camera.cpp112
-rw-r--r--examples/vulkan/hellovulkancubes/camera.h80
-rw-r--r--examples/vulkan/hellovulkancubes/color.frag12
-rw-r--r--examples/vulkan/hellovulkancubes/color.vert14
-rw-r--r--examples/vulkan/hellovulkancubes/color_frag.spvbin0 -> 616 bytes
-rw-r--r--examples/vulkan/hellovulkancubes/color_phong.frag39
-rw-r--r--examples/vulkan/hellovulkancubes/color_phong.vert32
-rw-r--r--examples/vulkan/hellovulkancubes/color_phong_frag.spvbin0 -> 3364 bytes
-rw-r--r--examples/vulkan/hellovulkancubes/color_phong_vert.spvbin0 -> 2268 bytes
-rw-r--r--examples/vulkan/hellovulkancubes/color_vert.spvbin0 -> 744 bytes
-rw-r--r--examples/vulkan/hellovulkancubes/hellovulkancubes.pro24
-rw-r--r--examples/vulkan/hellovulkancubes/hellovulkancubes.qrc10
-rw-r--r--examples/vulkan/hellovulkancubes/main.cpp92
-rw-r--r--examples/vulkan/hellovulkancubes/mainwindow.cpp117
-rw-r--r--examples/vulkan/hellovulkancubes/mainwindow.h83
-rw-r--r--examples/vulkan/hellovulkancubes/mesh.cpp98
-rw-r--r--examples/vulkan/hellovulkancubes/mesh.h79
-rw-r--r--examples/vulkan/hellovulkancubes/renderer.cpp1048
-rw-r--r--examples/vulkan/hellovulkancubes/renderer.h158
-rw-r--r--examples/vulkan/hellovulkancubes/shader.cpp94
-rw-r--r--examples/vulkan/hellovulkancubes/shader.h77
-rw-r--r--examples/vulkan/hellovulkancubes/vulkanwindow.cpp134
-rw-r--r--examples/vulkan/hellovulkancubes/vulkanwindow.h85
23 files changed, 2388 insertions, 0 deletions
diff --git a/examples/vulkan/hellovulkancubes/camera.cpp b/examples/vulkan/hellovulkancubes/camera.cpp
new file mode 100644
index 0000000000..64dee03154
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/camera.cpp
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** 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 "camera.h"
+
+Camera::Camera(const QVector3D &pos)
+ : m_forward(0.0f, 0.0f, -1.0f),
+ m_right(1.0f, 0.0f, 0.0f),
+ m_up(0.0f, 1.0f, 0.0f),
+ m_pos(pos),
+ m_yaw(0.0f),
+ m_pitch(0.0f)
+{
+}
+
+static inline void clamp360(float *v)
+{
+ if (*v > 360.0f)
+ *v -= 360.0f;
+ if (*v < -360.0f)
+ *v += 360.0f;
+}
+
+void Camera::yaw(float degrees)
+{
+ m_yaw += degrees;
+ clamp360(&m_yaw);
+ m_yawMatrix.setToIdentity();
+ m_yawMatrix.rotate(m_yaw, 0, 1, 0);
+
+ QMatrix4x4 rotMat = m_pitchMatrix * m_yawMatrix;
+ m_forward = (QVector4D(0.0f, 0.0f, -1.0f, 0.0f) * rotMat).toVector3D();
+ m_right = (QVector4D(1.0f, 0.0f, 0.0f, 0.0f) * rotMat).toVector3D();
+}
+
+void Camera::pitch(float degrees)
+{
+ m_pitch += degrees;
+ clamp360(&m_pitch);
+ m_pitchMatrix.setToIdentity();
+ m_pitchMatrix.rotate(m_pitch, 1, 0, 0);
+
+ QMatrix4x4 rotMat = m_pitchMatrix * m_yawMatrix;
+ m_forward = (QVector4D(0.0f, 0.0f, -1.0f, 0.0f) * rotMat).toVector3D();
+ m_up = (QVector4D(0.0f, 1.0f, 0.0f, 0.0f) * rotMat).toVector3D();
+}
+
+void Camera::walk(float amount)
+{
+ m_pos[0] += amount * m_forward.x();
+ m_pos[2] += amount * m_forward.z();
+}
+
+void Camera::strafe(float amount)
+{
+ m_pos[0] += amount * m_right.x();
+ m_pos[2] += amount * m_right.z();
+}
+
+QMatrix4x4 Camera::viewMatrix() const
+{
+ QMatrix4x4 m = m_pitchMatrix * m_yawMatrix;
+ m.translate(-m_pos);
+ return m;
+}
diff --git a/examples/vulkan/hellovulkancubes/camera.h b/examples/vulkan/hellovulkancubes/camera.h
new file mode 100644
index 0000000000..c5c579f066
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/camera.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef CAMERA_H
+#define CAMERA_H
+
+#include <QVector3D>
+#include <QMatrix4x4>
+
+class Camera
+{
+public:
+ Camera(const QVector3D &pos);
+
+ void yaw(float degrees);
+ void pitch(float degrees);
+ void walk(float amount);
+ void strafe(float amount);
+
+ QMatrix4x4 viewMatrix() const;
+
+private:
+ QVector3D m_forward;
+ QVector3D m_right;
+ QVector3D m_up;
+ QVector3D m_pos;
+ float m_yaw;
+ float m_pitch;
+ QMatrix4x4 m_yawMatrix;
+ QMatrix4x4 m_pitchMatrix;
+};
+
+#endif
diff --git a/examples/vulkan/hellovulkancubes/color.frag b/examples/vulkan/hellovulkancubes/color.frag
new file mode 100644
index 0000000000..3b04955963
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/color.frag
@@ -0,0 +1,12 @@
+#version 440
+
+layout(push_constant) uniform PC {
+ layout(offset = 64) vec3 color;
+} pc;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ fragColor = vec4(pc.color, 1.0);
+}
diff --git a/examples/vulkan/hellovulkancubes/color.vert b/examples/vulkan/hellovulkancubes/color.vert
new file mode 100644
index 0000000000..19bf815819
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/color.vert
@@ -0,0 +1,14 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+
+out gl_PerVertex { vec4 gl_Position; };
+
+layout(push_constant) uniform PC {
+ mat4 mvp;
+} pc;
+
+void main()
+{
+ gl_Position = pc.mvp * position;
+}
diff --git a/examples/vulkan/hellovulkancubes/color_frag.spv b/examples/vulkan/hellovulkancubes/color_frag.spv
new file mode 100644
index 0000000000..bd72984ffe
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/color_frag.spv
Binary files differ
diff --git a/examples/vulkan/hellovulkancubes/color_phong.frag b/examples/vulkan/hellovulkancubes/color_phong.frag
new file mode 100644
index 0000000000..8b0c715f3b
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/color_phong.frag
@@ -0,0 +1,39 @@
+#version 440
+
+layout(location = 0) in vec3 vECVertNormal;
+layout(location = 1) in vec3 vECVertPos;
+layout(location = 2) flat in vec3 vDiffuseAdjust;
+
+layout(std140, binding = 1) uniform buf {
+ vec3 ECCameraPosition;
+ vec3 ka;
+ vec3 kd;
+ vec3 ks;
+ // Have one light only for now.
+ vec3 ECLightPosition;
+ vec3 attenuation;
+ vec3 color;
+ float intensity;
+ float specularExp;
+} ubuf;
+
+layout(location = 0) out vec4 fragColor;
+
+void main()
+{
+ vec3 unnormL = ubuf.ECLightPosition - vECVertPos;
+ float dist = length(unnormL);
+ float att = 1.0 / (ubuf.attenuation.x + ubuf.attenuation.y * dist + ubuf.attenuation.z * dist * dist);
+
+ vec3 N = normalize(vECVertNormal);
+ vec3 L = normalize(unnormL);
+ float NL = max(0.0, dot(N, L));
+ vec3 dColor = att * ubuf.intensity * ubuf.color * NL;
+
+ vec3 R = reflect(-L, N);
+ vec3 V = normalize(ubuf.ECCameraPosition - vECVertPos);
+ float RV = max(0.0, dot(R, V));
+ vec3 sColor = att * ubuf.intensity * ubuf.color * pow(RV, ubuf.specularExp);
+
+ fragColor = vec4(ubuf.ka + (ubuf.kd + vDiffuseAdjust) * dColor + ubuf.ks * sColor, 1.0);
+}
diff --git a/examples/vulkan/hellovulkancubes/color_phong.vert b/examples/vulkan/hellovulkancubes/color_phong.vert
new file mode 100644
index 0000000000..a1d1552685
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/color_phong.vert
@@ -0,0 +1,32 @@
+#version 440
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec3 normal;
+
+// Instanced attributes to variate the translation of the model and the diffuse
+// color of the material.
+layout(location = 2) in vec3 instTranslate;
+layout(location = 3) in vec3 instDiffuseAdjust;
+
+out gl_PerVertex { vec4 gl_Position; };
+layout(location = 0) out vec3 vECVertNormal;
+layout(location = 1) out vec3 vECVertPos;
+layout(location = 2) flat out vec3 vDiffuseAdjust;
+
+layout(std140, binding = 0) uniform buf {
+ mat4 vp;
+ mat4 model;
+ mat3 modelNormal;
+} ubuf;
+
+void main()
+{
+ vECVertNormal = normalize(ubuf.modelNormal * normal);
+ mat4 t = mat4(1, 0, 0, 0,
+ 0, 1, 0, 0,
+ 0, 0, 1, 0,
+ instTranslate.x, instTranslate.y, instTranslate.z, 1);
+ vECVertPos = vec3(t * ubuf.model * position);
+ vDiffuseAdjust = instDiffuseAdjust;
+ gl_Position = ubuf.vp * t * ubuf.model * position;
+}
diff --git a/examples/vulkan/hellovulkancubes/color_phong_frag.spv b/examples/vulkan/hellovulkancubes/color_phong_frag.spv
new file mode 100644
index 0000000000..a1a413533b
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/color_phong_frag.spv
Binary files differ
diff --git a/examples/vulkan/hellovulkancubes/color_phong_vert.spv b/examples/vulkan/hellovulkancubes/color_phong_vert.spv
new file mode 100644
index 0000000000..3ede21e007
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/color_phong_vert.spv
Binary files differ
diff --git a/examples/vulkan/hellovulkancubes/color_vert.spv b/examples/vulkan/hellovulkancubes/color_vert.spv
new file mode 100644
index 0000000000..3f708b7d07
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/color_vert.spv
Binary files differ
diff --git a/examples/vulkan/hellovulkancubes/hellovulkancubes.pro b/examples/vulkan/hellovulkancubes/hellovulkancubes.pro
new file mode 100644
index 0000000000..f9a9c3cff1
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/hellovulkancubes.pro
@@ -0,0 +1,24 @@
+QT += widgets concurrent
+
+HEADERS += \
+ mainwindow.h \
+ vulkanwindow.h \
+ renderer.h \
+ mesh.h \
+ shader.h \
+ camera.h
+
+SOURCES += \
+ main.cpp \
+ mainwindow.cpp \
+ vulkanwindow.cpp \
+ renderer.cpp \
+ mesh.cpp \
+ shader.cpp \
+ camera.cpp
+
+RESOURCES += hellovulkancubes.qrc
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/vulkan/hellovulkancubes
+INSTALLS += target
diff --git a/examples/vulkan/hellovulkancubes/hellovulkancubes.qrc b/examples/vulkan/hellovulkancubes/hellovulkancubes.qrc
new file mode 100644
index 0000000000..7b085e1875
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/hellovulkancubes.qrc
@@ -0,0 +1,10 @@
+<!DOCTYPE RCC><RCC version="1.0">
+<qresource>
+ <file alias="block.buf">../shared/block.buf</file>
+ <file alias="qt_logo.buf">../shared/qt_logo.buf</file>
+ <file>color_phong_vert.spv</file>
+ <file>color_phong_frag.spv</file>
+ <file>color_vert.spv</file>
+ <file>color_frag.spv</file>
+</qresource>
+</RCC>
diff --git a/examples/vulkan/hellovulkancubes/main.cpp b/examples/vulkan/hellovulkancubes/main.cpp
new file mode 100644
index 0000000000..4ec4d48645
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/main.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** 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 <QLoggingCategory>
+#include "mainwindow.h"
+#include "vulkanwindow.h"
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ const bool dbg = qEnvironmentVariableIntValue("QT_VK_DEBUG");
+
+ QVulkanInstance inst;
+
+ if (dbg) {
+ QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true"));
+
+#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(dbg);
+ vulkanWindow->setVulkanInstance(&inst);
+
+ MainWindow mainWindow(vulkanWindow);
+ mainWindow.resize(1024, 768);
+ mainWindow.show();
+
+ return app.exec();
+}
diff --git a/examples/vulkan/hellovulkancubes/mainwindow.cpp b/examples/vulkan/hellovulkancubes/mainwindow.cpp
new file mode 100644
index 0000000000..2be7d237af
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/mainwindow.cpp
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** 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 "mainwindow.h"
+#include "vulkanwindow.h"
+#include <QApplication>
+#include <QLabel>
+#include <QPushButton>
+#include <QLCDNumber>
+#include <QCheckBox>
+#include <QGridLayout>
+
+MainWindow::MainWindow(VulkanWindow *vulkanWindow)
+{
+ QWidget *wrapper = QWidget::createWindowContainer(vulkanWindow);
+ wrapper->setFocusPolicy(Qt::StrongFocus);
+ wrapper->setFocus();
+
+ infoLabel = new QLabel;
+ infoLabel->setFrameStyle(QFrame::Box | QFrame::Raised);
+ infoLabel->setAlignment(Qt::AlignCenter);
+ infoLabel->setText(tr("This example demonstrates instanced drawing\nof a mesh loaded from a file.\n"
+ "Uses a Phong material with a single light.\n"
+ "Also demonstrates dynamic uniform buffers\nand a bit of threading with QtConcurrent.\n"
+ "Uses 4x MSAA when available.\n"
+ "Comes with an FPS camera.\n"
+ "Hit [Shift+]WASD to walk and strafe.\nPress and move mouse to look around.\n"
+ "Click Add New to increase the number of instances."));
+
+ meshSwitch = new QCheckBox(tr("&Use Qt logo"));
+ meshSwitch->setFocusPolicy(Qt::NoFocus); // do not interfere with vulkanWindow's keyboard input
+
+ counterLcd = new QLCDNumber(5);
+ counterLcd->setSegmentStyle(QLCDNumber::Filled);
+ counterLcd->display(m_count);
+
+ newButton = new QPushButton(tr("&Add new"));
+ newButton->setFocusPolicy(Qt::NoFocus);
+ quitButton = new QPushButton(tr("&Quit"));
+ quitButton->setFocusPolicy(Qt::NoFocus);
+ pauseButton = new QPushButton(tr("&Pause"));
+ pauseButton->setFocusPolicy(Qt::NoFocus);
+
+ connect(quitButton, &QPushButton::clicked, qApp, &QCoreApplication::quit);
+ connect(newButton, &QPushButton::clicked, vulkanWindow, [=] {
+ vulkanWindow->addNew();
+ m_count = vulkanWindow->instanceCount();
+ counterLcd->display(m_count);
+ });
+ connect(pauseButton, &QPushButton::clicked, vulkanWindow, &VulkanWindow::togglePaused);
+ connect(meshSwitch, &QCheckBox::clicked, vulkanWindow, &VulkanWindow::meshSwitched);
+
+ QGridLayout *layout = new QGridLayout;
+ layout->addWidget(infoLabel, 0, 2);
+ layout->addWidget(meshSwitch, 1, 2);
+ layout->addWidget(createLabel(tr("INSTANCES")), 2, 2);
+ layout->addWidget(counterLcd, 3, 2);
+ layout->addWidget(newButton, 4, 2);
+ layout->addWidget(pauseButton, 5, 2);
+ layout->addWidget(quitButton, 6, 2);
+ layout->addWidget(wrapper, 0, 0, 7, 2);
+ setLayout(layout);
+}
+
+QLabel *MainWindow::createLabel(const QString &text)
+{
+ QLabel *lbl = new QLabel(text);
+ lbl->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
+ return lbl;
+}
diff --git a/examples/vulkan/hellovulkancubes/mainwindow.h b/examples/vulkan/hellovulkancubes/mainwindow.h
new file mode 100644
index 0000000000..4109709959
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/mainwindow.h
@@ -0,0 +1,83 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include <QWidget>
+
+QT_BEGIN_NAMESPACE
+class QLCDNumber;
+class QLabel;
+class QPushButton;
+class QCheckBox;
+QT_END_NAMESPACE
+
+class VulkanWindow;
+
+class MainWindow : public QWidget
+{
+public:
+ MainWindow(VulkanWindow *vulkanWindow);
+
+private:
+ QLabel *createLabel(const QString &text);
+
+ QLabel *infoLabel;
+ QCheckBox *meshSwitch;
+ QLCDNumber *counterLcd;
+ QPushButton *newButton;
+ QPushButton *quitButton;
+ QPushButton *pauseButton;
+
+ int m_count = 128;
+};
+
+#endif
diff --git a/examples/vulkan/hellovulkancubes/mesh.cpp b/examples/vulkan/hellovulkancubes/mesh.cpp
new file mode 100644
index 0000000000..fcc45bfd57
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/mesh.cpp
@@ -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 "mesh.h"
+#include <QtConcurrentRun>
+#include <QFile>
+
+void Mesh::load(const QString &fn)
+{
+ reset();
+ m_maybeRunning = true;
+ m_future = QtConcurrent::run([fn]() {
+ MeshData md;
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("Failed to open %s", qPrintable(fn));
+ return md;
+ }
+ QByteArray buf = f.readAll();
+ const char *p = buf.constData();
+ quint32 format;
+ memcpy(&format, p, 4);
+ if (format != 1) {
+ qWarning("Invalid format in %s", qPrintable(fn));
+ return md;
+ }
+ int ofs = 4;
+ memcpy(&md.vertexCount, p + ofs, 4);
+ ofs += 4;
+ memcpy(md.aabb, p + ofs, 6 * 4);
+ ofs += 6 * 4;
+ const int byteCount = md.vertexCount * 8 * 4;
+ md.geom.resize(byteCount);
+ memcpy(md.geom.data(), p + ofs, byteCount);
+ return md;
+ });
+}
+
+MeshData *Mesh::data()
+{
+ if (m_maybeRunning && !m_data.isValid())
+ m_data = m_future.result();
+
+ return &m_data;
+}
+
+void Mesh::reset()
+{
+ *data() = MeshData();
+ m_maybeRunning = false;
+}
diff --git a/examples/vulkan/hellovulkancubes/mesh.h b/examples/vulkan/hellovulkancubes/mesh.h
new file mode 100644
index 0000000000..cb6ee9c830
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/mesh.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef MESH_H
+#define MESH_H
+
+#include <QString>
+#include <QFuture>
+
+struct MeshData
+{
+ bool isValid() const { return vertexCount > 0; }
+ int vertexCount = 0;
+ float aabb[6];
+ QByteArray geom; // x, y, z, u, v, nx, ny, nz
+};
+
+class Mesh
+{
+public:
+ void load(const QString &fn);
+ MeshData *data();
+ bool isValid() { return data()->isValid(); }
+ void reset();
+
+private:
+ bool m_maybeRunning = false;
+ QFuture<MeshData> m_future;
+ MeshData m_data;
+};
+
+#endif
diff --git a/examples/vulkan/hellovulkancubes/renderer.cpp b/examples/vulkan/hellovulkancubes/renderer.cpp
new file mode 100644
index 0000000000..96ae6020a7
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/renderer.cpp
@@ -0,0 +1,1048 @@
+/****************************************************************************
+**
+** 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 "renderer.h"
+#include <QVulkanFunctions>
+#include <QtConcurrentRun>
+#include <QTime>
+
+static float quadVert[] = {
+ -1, -1, 0,
+ -1, 1, 0,
+ 1, -1, 0,
+ 1, 1, 0
+};
+
+#define DBG Q_UNLIKELY(m_window->isDebugEnabled())
+
+const int MAX_INSTANCES = 16384;
+const VkDeviceSize PER_INSTANCE_DATA_SIZE = 6 * sizeof(float); // instTranslate, instDiffuseAdjust
+
+static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
+{
+ return (v + byteAlign - 1) & ~(byteAlign - 1);
+}
+
+Renderer::Renderer(VulkanWindow *w, int initialCount)
+ : m_window(w),
+ // Have the light positioned just behind the default camera position, looking forward.
+ m_lightPos(0.0f, 0.0f, 25.0f),
+ m_cam(QVector3D(0.0f, 0.0f, 20.0f)), // starting camera position
+ m_instCount(initialCount)
+{
+ qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));
+
+ m_floorModel.translate(0, -5, 0);
+ m_floorModel.rotate(-90, 1, 0, 0);
+ m_floorModel.scale(20, 100, 1);
+
+ m_blockMesh.load(QStringLiteral(":/block.buf"));
+ m_logoMesh.load(QStringLiteral(":/qt_logo.buf"));
+
+ QObject::connect(&m_frameWatcher, &QFutureWatcherBase::finished, [this] {
+ if (m_framePending) {
+ m_framePending = false;
+ m_window->frameReady();
+ m_window->requestUpdate();
+ }
+ });
+}
+
+void Renderer::preInitResources()
+{
+ QSet<int> sampleCounts = m_window->supportedSampleCounts();
+ if (DBG)
+ qDebug() << "Supported sample counts:" << sampleCounts;
+ if (sampleCounts.contains(4)) {
+ if (DBG)
+ qDebug("Requesting 4x MSAA");
+ m_window->setSampleCount(4);
+ }
+}
+
+void Renderer::initResources()
+{
+ if (DBG)
+ qDebug("Renderer init");
+
+ m_animating = true;
+ m_framePending = false;
+
+ QVulkanInstance *inst = m_window->vulkanInstance();
+ VkDevice dev = m_window->device();
+ const VkPhysicalDeviceLimits *pdevLimits = &m_window->physicalDeviceProperties()->limits;
+ const VkDeviceSize uniAlign = pdevLimits->minUniformBufferOffsetAlignment;
+
+ m_devFuncs = inst->deviceFunctions(dev);
+
+ // Note the std140 packing rules. A vec3 still has an alignment of 16,
+ // while a mat3 is like 3 * vec3.
+ m_itemMaterial.vertUniSize = aligned(2 * 64 + 48, uniAlign); // see color_phong.vert
+ m_itemMaterial.fragUniSize = aligned(6 * 16 + 12 + 2 * 4, uniAlign); // see color_phong.frag
+
+ if (!m_itemMaterial.vs.isValid())
+ m_itemMaterial.vs.load(inst, dev, QStringLiteral(":/color_phong_vert.spv"));
+ if (!m_itemMaterial.fs.isValid())
+ m_itemMaterial.fs.load(inst, dev, QStringLiteral(":/color_phong_frag.spv"));
+
+ if (!m_floorMaterial.vs.isValid())
+ m_floorMaterial.vs.load(inst, dev, QStringLiteral(":/color_vert.spv"));
+ if (!m_floorMaterial.fs.isValid())
+ m_floorMaterial.fs.load(inst, dev, QStringLiteral(":/color_frag.spv"));
+
+ m_pipelinesFuture = QtConcurrent::run(this, &Renderer::createPipelines);
+}
+
+void Renderer::createPipelines()
+{
+ VkDevice dev = m_window->device();
+
+ VkPipelineCacheCreateInfo pipelineCacheInfo;
+ memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo));
+ pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
+ VkResult err = m_devFuncs->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &m_pipelineCache);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create pipeline cache: %d", err);
+
+ createItemPipeline();
+ createFloorPipeline();
+}
+
+void Renderer::createItemPipeline()
+{
+ VkDevice dev = m_window->device();
+
+ // Vertex layout.
+ VkVertexInputBindingDescription vertexBindingDesc[] = {
+ {
+ 0, // binding
+ 8 * sizeof(float),
+ VK_VERTEX_INPUT_RATE_VERTEX
+ },
+ {
+ 1,
+ 6 * sizeof(float),
+ VK_VERTEX_INPUT_RATE_INSTANCE
+ }
+ };
+ VkVertexInputAttributeDescription vertexAttrDesc[] = {
+ { // position
+ 0, // location
+ 0, // binding
+ VK_FORMAT_R32G32B32_SFLOAT,
+ 0 // offset
+ },
+ { // normal
+ 1,
+ 0,
+ VK_FORMAT_R32G32B32_SFLOAT,
+ 5 * sizeof(float)
+ },
+ { // instTranslate
+ 2,
+ 1,
+ VK_FORMAT_R32G32B32_SFLOAT,
+ 0
+ },
+ { // instDiffuseAdjust
+ 3,
+ 1,
+ VK_FORMAT_R32G32B32_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 = sizeof(vertexBindingDesc) / sizeof(vertexBindingDesc[0]);
+ vertexInputInfo.pVertexBindingDescriptions = vertexBindingDesc;
+ vertexInputInfo.vertexAttributeDescriptionCount = sizeof(vertexAttrDesc) / sizeof(vertexAttrDesc[0]);
+ vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc;
+
+ // Descriptor set layout.
+ VkDescriptorPoolSize descPoolSizes[] = {
+ { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 2 }
+ };
+ VkDescriptorPoolCreateInfo descPoolInfo;
+ memset(&descPoolInfo, 0, sizeof(descPoolInfo));
+ descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ descPoolInfo.maxSets = 1; // a single set is enough due to the dynamic uniform buffer
+ descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]);
+ descPoolInfo.pPoolSizes = descPoolSizes;
+ VkResult err = m_devFuncs->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, &m_itemMaterial.descPool);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create descriptor pool: %d", err);
+
+ VkDescriptorSetLayoutBinding layoutBindings[] =
+ {
+ {
+ 0, // binding
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
+ 1, // descriptorCount
+ VK_SHADER_STAGE_VERTEX_BIT,
+ nullptr
+ },
+ {
+ 1,
+ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
+ 1,
+ VK_SHADER_STAGE_FRAGMENT_BIT,
+ nullptr
+ }
+ };
+ VkDescriptorSetLayoutCreateInfo descLayoutInfo = {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
+ nullptr,
+ 0,
+ sizeof(layoutBindings) / sizeof(layoutBindings[0]),
+ layoutBindings
+ };
+ err = m_devFuncs->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_itemMaterial.descSetLayout);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create descriptor set layout: %d", err);
+
+ VkDescriptorSetAllocateInfo descSetAllocInfo = {
+ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
+ nullptr,
+ m_itemMaterial.descPool,
+ 1,
+ &m_itemMaterial.descSetLayout
+ };
+ err = m_devFuncs->vkAllocateDescriptorSets(dev, &descSetAllocInfo, &m_itemMaterial.descSet);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to allocate descriptor set: %d", err);
+
+ // Graphics pipeline.
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo;
+ memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
+ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutInfo.setLayoutCount = 1;
+ pipelineLayoutInfo.pSetLayouts = &m_itemMaterial.descSetLayout;
+
+ err = m_devFuncs->vkCreatePipelineLayout(dev, &pipelineLayoutInfo, nullptr, &m_itemMaterial.pipelineLayout);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create pipeline layout: %d", err);
+
+ 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,
+ m_itemMaterial.vs.data()->shaderModule,
+ "main",
+ nullptr
+ },
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ nullptr,
+ 0,
+ VK_SHADER_STAGE_FRAGMENT_BIT,
+ m_itemMaterial.fs.data()->shaderModule,
+ "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;
+
+ 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_BACK_BIT;
+ 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 = 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;
+ 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_itemMaterial.pipelineLayout;
+ pipelineInfo.renderPass = m_window->defaultRenderPass();
+
+ err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_itemMaterial.pipeline);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create graphics pipeline: %d", err);
+}
+
+void Renderer::createFloorPipeline()
+{
+ VkDevice dev = m_window->device();
+
+ // Vertex layout.
+ VkVertexInputBindingDescription vertexBindingDesc = {
+ 0, // binding
+ 3 * sizeof(float),
+ VK_VERTEX_INPUT_RATE_VERTEX
+ };
+ VkVertexInputAttributeDescription vertexAttrDesc[] = {
+ { // position
+ 0, // location
+ 0, // binding
+ VK_FORMAT_R32G32B32_SFLOAT,
+ 0 // offset
+ },
+ };
+
+ 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 = sizeof(vertexAttrDesc) / sizeof(vertexAttrDesc[0]);
+ vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc;
+
+ // Do not bother with uniform buffers and descriptors, all the data fits
+ // into the spec mandated minimum of 128 bytes for push constants.
+ VkPushConstantRange pcr[] = {
+ // mvp
+ {
+ VK_SHADER_STAGE_VERTEX_BIT,
+ 0,
+ 64
+ },
+ // color
+ {
+ VK_SHADER_STAGE_FRAGMENT_BIT,
+ 64,
+ 12
+ }
+ };
+
+ VkPipelineLayoutCreateInfo pipelineLayoutInfo;
+ memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
+ pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutInfo.pushConstantRangeCount = sizeof(pcr) / sizeof(pcr[0]);
+ pipelineLayoutInfo.pPushConstantRanges = pcr;
+
+ VkResult err = m_devFuncs->vkCreatePipelineLayout(dev, &pipelineLayoutInfo, nullptr, &m_floorMaterial.pipelineLayout);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create pipeline layout: %d", err);
+
+ 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,
+ m_floorMaterial.vs.data()->shaderModule,
+ "main",
+ nullptr
+ },
+ {
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
+ nullptr,
+ 0,
+ VK_SHADER_STAGE_FRAGMENT_BIT,
+ m_floorMaterial.fs.data()->shaderModule,
+ "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;
+
+ 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_BACK_BIT;
+ rs.frontFace = VK_FRONT_FACE_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 = 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;
+ 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_floorMaterial.pipelineLayout;
+ pipelineInfo.renderPass = m_window->defaultRenderPass();
+
+ err = m_devFuncs->vkCreateGraphicsPipelines(dev, m_pipelineCache, 1, &pipelineInfo, nullptr, &m_floorMaterial.pipeline);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create graphics pipeline: %d", err);
+}
+
+void Renderer::initSwapChainResources()
+{
+ m_proj = *m_window->clipCorrectionMatrix();
+ const QSize sz = m_window->swapChainImageSize();
+ m_proj.perspective(45.0f, sz.width() / (float) sz.height(), 0.01f, 1000.0f);
+ markViewProjDirty();
+}
+
+void Renderer::releaseSwapChainResources()
+{
+ // It is important to finish the pending frame right here since this is the
+ // last opportunity to act with all resources intact.
+ m_frameWatcher.waitForFinished();
+ // Cannot count on the finished() signal being emitted before returning
+ // from here.
+ if (m_framePending) {
+ m_framePending = false;
+ m_window->frameReady();
+ }
+}
+
+void Renderer::releaseResources()
+{
+ if (DBG)
+ qDebug("Renderer release");
+
+ m_pipelinesFuture.waitForFinished();
+
+ VkDevice dev = m_window->device();
+
+ if (m_itemMaterial.descSetLayout) {
+ m_devFuncs->vkDestroyDescriptorSetLayout(dev, m_itemMaterial.descSetLayout, nullptr);
+ m_itemMaterial.descSetLayout = VK_NULL_HANDLE;
+ }
+
+ if (m_itemMaterial.descPool) {
+ m_devFuncs->vkDestroyDescriptorPool(dev, m_itemMaterial.descPool, nullptr);
+ m_itemMaterial.descPool = VK_NULL_HANDLE;
+ }
+
+ if (m_itemMaterial.pipeline) {
+ m_devFuncs->vkDestroyPipeline(dev, m_itemMaterial.pipeline, nullptr);
+ m_itemMaterial.pipeline = VK_NULL_HANDLE;
+ }
+
+ if (m_itemMaterial.pipelineLayout) {
+ m_devFuncs->vkDestroyPipelineLayout(dev, m_itemMaterial.pipelineLayout, nullptr);
+ m_itemMaterial.pipelineLayout = VK_NULL_HANDLE;
+ }
+
+ if (m_floorMaterial.pipeline) {
+ m_devFuncs->vkDestroyPipeline(dev, m_floorMaterial.pipeline, nullptr);
+ m_floorMaterial.pipeline = VK_NULL_HANDLE;
+ }
+
+ if (m_floorMaterial.pipelineLayout) {
+ m_devFuncs->vkDestroyPipelineLayout(dev, m_floorMaterial.pipelineLayout, nullptr);
+ m_floorMaterial.pipelineLayout = VK_NULL_HANDLE;
+ }
+
+ if (m_pipelineCache) {
+ m_devFuncs->vkDestroyPipelineCache(dev, m_pipelineCache, nullptr);
+ m_pipelineCache = VK_NULL_HANDLE;
+ }
+
+ if (m_blockVertexBuf) {
+ m_devFuncs->vkDestroyBuffer(dev, m_blockVertexBuf, nullptr);
+ m_blockVertexBuf = VK_NULL_HANDLE;
+ }
+
+ if (m_logoVertexBuf) {
+ m_devFuncs->vkDestroyBuffer(dev, m_logoVertexBuf, nullptr);
+ m_logoVertexBuf = VK_NULL_HANDLE;
+ }
+
+ if (m_floorVertexBuf) {
+ m_devFuncs->vkDestroyBuffer(dev, m_floorVertexBuf, nullptr);
+ m_floorVertexBuf = VK_NULL_HANDLE;
+ }
+
+ if (m_uniBuf) {
+ m_devFuncs->vkDestroyBuffer(dev, m_uniBuf, nullptr);
+ m_uniBuf = VK_NULL_HANDLE;
+ }
+
+ if (m_bufMem) {
+ m_devFuncs->vkFreeMemory(dev, m_bufMem, nullptr);
+ m_bufMem = VK_NULL_HANDLE;
+ }
+
+ if (m_instBuf) {
+ m_devFuncs->vkDestroyBuffer(dev, m_instBuf, nullptr);
+ m_instBuf = VK_NULL_HANDLE;
+ }
+
+ if (m_instBufMem) {
+ m_devFuncs->vkFreeMemory(dev, m_instBufMem, nullptr);
+ m_instBufMem = VK_NULL_HANDLE;
+ }
+
+ if (m_itemMaterial.vs.isValid()) {
+ m_devFuncs->vkDestroyShaderModule(dev, m_itemMaterial.vs.data()->shaderModule, nullptr);
+ m_itemMaterial.vs.reset();
+ }
+ if (m_itemMaterial.fs.isValid()) {
+ m_devFuncs->vkDestroyShaderModule(dev, m_itemMaterial.fs.data()->shaderModule, nullptr);
+ m_itemMaterial.fs.reset();
+ }
+
+ if (m_floorMaterial.vs.isValid()) {
+ m_devFuncs->vkDestroyShaderModule(dev, m_floorMaterial.vs.data()->shaderModule, nullptr);
+ m_floorMaterial.vs.reset();
+ }
+ if (m_floorMaterial.fs.isValid()) {
+ m_devFuncs->vkDestroyShaderModule(dev, m_floorMaterial.fs.data()->shaderModule, nullptr);
+ m_floorMaterial.fs.reset();
+ }
+}
+
+void Renderer::ensureBuffers()
+{
+ if (m_blockVertexBuf)
+ return;
+
+ VkDevice dev = m_window->device();
+ const int concurrentFrameCount = m_window->concurrentFrameCount();
+
+ // Vertex buffer for the block.
+ VkBufferCreateInfo bufInfo;
+ memset(&bufInfo, 0, sizeof(bufInfo));
+ bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ const int blockMeshByteCount = m_blockMesh.data()->vertexCount * 8 * sizeof(float);
+ bufInfo.size = blockMeshByteCount;
+ bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ VkResult err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_blockVertexBuf);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create vertex buffer: %d", err);
+
+ VkMemoryRequirements blockVertMemReq;
+ m_devFuncs->vkGetBufferMemoryRequirements(dev, m_blockVertexBuf, &blockVertMemReq);
+
+ // Vertex buffer for the logo.
+ const int logoMeshByteCount = m_logoMesh.data()->vertexCount * 8 * sizeof(float);
+ bufInfo.size = logoMeshByteCount;
+ bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+ err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_logoVertexBuf);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create vertex buffer: %d", err);
+
+ VkMemoryRequirements logoVertMemReq;
+ m_devFuncs->vkGetBufferMemoryRequirements(dev, m_logoVertexBuf, &logoVertMemReq);
+
+ // Vertex buffer for the floor.
+ bufInfo.size = sizeof(quadVert);
+ err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_floorVertexBuf);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create vertex buffer: %d", err);
+
+ VkMemoryRequirements floorVertMemReq;
+ m_devFuncs->vkGetBufferMemoryRequirements(dev, m_floorVertexBuf, &floorVertMemReq);
+
+ // Uniform buffer. Instead of using multiple descriptor sets, we take a
+ // different approach: have a single dynamic uniform buffer and specify the
+ // active-frame-specific offset at the time of binding the descriptor set.
+ bufInfo.size = (m_itemMaterial.vertUniSize + m_itemMaterial.fragUniSize) * concurrentFrameCount;
+ bufInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_uniBuf);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create uniform buffer: %d", err);
+
+ VkMemoryRequirements uniMemReq;
+ m_devFuncs->vkGetBufferMemoryRequirements(dev, m_uniBuf, &uniMemReq);
+
+ // Allocate memory for everything at once.
+ VkDeviceSize logoVertStartOffset = aligned(0 + blockVertMemReq.size, logoVertMemReq.alignment);
+ VkDeviceSize floorVertStartOffset = aligned(logoVertStartOffset + logoVertMemReq.size, floorVertMemReq.alignment);
+ m_itemMaterial.uniMemStartOffset = aligned(floorVertStartOffset + floorVertMemReq.size, uniMemReq.alignment);
+ VkMemoryAllocateInfo memAllocInfo = {
+ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ nullptr,
+ m_itemMaterial.uniMemStartOffset + uniMemReq.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_blockVertexBuf, m_bufMem, 0);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to bind vertex buffer memory: %d", err);
+ err = m_devFuncs->vkBindBufferMemory(dev, m_logoVertexBuf, m_bufMem, logoVertStartOffset);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to bind vertex buffer memory: %d", err);
+ err = m_devFuncs->vkBindBufferMemory(dev, m_floorVertexBuf, m_bufMem, floorVertStartOffset);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to bind vertex buffer memory: %d", err);
+ err = m_devFuncs->vkBindBufferMemory(dev, m_uniBuf, m_bufMem, m_itemMaterial.uniMemStartOffset);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to bind uniform buffer memory: %d", err);
+
+ // Copy vertex data.
+ quint8 *p;
+ err = m_devFuncs->vkMapMemory(dev, m_bufMem, 0, m_itemMaterial.uniMemStartOffset, 0, reinterpret_cast<void **>(&p));
+ if (err != VK_SUCCESS)
+ qFatal("Failed to map memory: %d", err);
+ memcpy(p, m_blockMesh.data()->geom.constData(), blockMeshByteCount);
+ memcpy(p + logoVertStartOffset, m_logoMesh.data()->geom.constData(), logoMeshByteCount);
+ memcpy(p + floorVertStartOffset, quadVert, sizeof(quadVert));
+ m_devFuncs->vkUnmapMemory(dev, m_bufMem);
+
+ // Write descriptors for the uniform buffers in the vertex and fragment shaders.
+ VkDescriptorBufferInfo vertUni = { m_uniBuf, 0, m_itemMaterial.vertUniSize };
+ VkDescriptorBufferInfo fragUni = { m_uniBuf, m_itemMaterial.vertUniSize, m_itemMaterial.fragUniSize };
+
+ VkWriteDescriptorSet descWrite[2];
+ memset(descWrite, 0, sizeof(descWrite));
+ descWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrite[0].dstSet = m_itemMaterial.descSet;
+ descWrite[0].dstBinding = 0;
+ descWrite[0].descriptorCount = 1;
+ descWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+ descWrite[0].pBufferInfo = &vertUni;
+
+ descWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ descWrite[1].dstSet = m_itemMaterial.descSet;
+ descWrite[1].dstBinding = 1;
+ descWrite[1].descriptorCount = 1;
+ descWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+ descWrite[1].pBufferInfo = &fragUni;
+
+ m_devFuncs->vkUpdateDescriptorSets(dev, 2, descWrite, 0, nullptr);
+}
+
+void Renderer::ensureInstanceBuffer()
+{
+ if (m_instCount == m_preparedInstCount && m_instBuf)
+ return;
+
+ Q_ASSERT(m_instCount <= MAX_INSTANCES);
+
+ VkDevice dev = m_window->device();
+
+ // allocate only once, for the maximum instance count
+ if (!m_instBuf) {
+ VkBufferCreateInfo bufInfo;
+ memset(&bufInfo, 0, sizeof(bufInfo));
+ bufInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
+ bufInfo.size = MAX_INSTANCES * PER_INSTANCE_DATA_SIZE;
+ bufInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
+
+ // Keep a copy of the data since we may lose all graphics resources on
+ // unexpose, and reinitializing to new random positions afterwards
+ // would not be nice.
+ m_instData.resize(bufInfo.size);
+
+ VkResult err = m_devFuncs->vkCreateBuffer(dev, &bufInfo, nullptr, &m_instBuf);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to create instance buffer: %d", err);
+
+ VkMemoryRequirements memReq;
+ m_devFuncs->vkGetBufferMemoryRequirements(dev, m_instBuf, &memReq);
+ if (DBG)
+ qDebug("Allocating %u bytes for instance data", uint32_t(memReq.size));
+
+ VkMemoryAllocateInfo memAllocInfo = {
+ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
+ nullptr,
+ memReq.size,
+ m_window->hostVisibleMemoryIndex()
+ };
+ err = m_devFuncs->vkAllocateMemory(dev, &memAllocInfo, nullptr, &m_instBufMem);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to allocate memory: %d", err);
+
+ err = m_devFuncs->vkBindBufferMemory(dev, m_instBuf, m_instBufMem, 0);
+ if (err != VK_SUCCESS)
+ qFatal("Failed to bind instance buffer memory: %d", err);
+ }
+
+ if (m_instCount != m_preparedInstCount) {
+ if (DBG)
+ qDebug("Preparing instances %d..%d", m_preparedInstCount, m_instCount - 1);
+ char *p = m_instData.data();
+ p += m_preparedInstCount * PER_INSTANCE_DATA_SIZE;
+ auto gen = [](float a, float b) { return float((qrand() % int(b - a + 1)) + a); };
+ for (int i = m_preparedInstCount; i < m_instCount; ++i) {
+ // Apply a random translation to each instance of the mesh.
+ float t[] = { gen(-5, 5), gen(-4, 6), gen(-30, 5) };
+ memcpy(p, t, 12);
+ // Apply a random adjustment to the diffuse color for each instance. (default is 0.7)
+ float d[] = { gen(-6, 3) / 10.0f, gen(-6, 3) / 10.0f, gen(-6, 3) / 10.0f };
+ memcpy(p + 12, d, 12);
+ p += PER_INSTANCE_DATA_SIZE;
+ }
+ m_preparedInstCount = m_instCount;
+ }
+
+ quint8 *p;
+ VkResult err = m_devFuncs->vkMapMemory(dev, m_instBufMem, 0, m_instCount * PER_INSTANCE_DATA_SIZE, 0,
+ reinterpret_cast<void **>(&p));
+ if (err != VK_SUCCESS)
+ qFatal("Failed to map memory: %d", err);
+ memcpy(p, m_instData.constData(), m_instData.size());
+ m_devFuncs->vkUnmapMemory(dev, m_instBufMem);
+}
+
+void Renderer::getMatrices(QMatrix4x4 *vp, QMatrix4x4 *model, QMatrix3x3 *modelNormal, QVector3D *eyePos)
+{
+ model->setToIdentity();
+ if (m_useLogo)
+ model->rotate(90, 1, 0, 0);
+ model->rotate(m_rotation, 1, 1, 0);
+
+ *modelNormal = model->normalMatrix();
+
+ QMatrix4x4 view = m_cam.viewMatrix();
+ *vp = m_proj * view;
+
+ *eyePos = view.inverted().column(3).toVector3D();
+}
+
+void Renderer::writeFragUni(quint8 *p, const QVector3D &eyePos)
+{
+ float ECCameraPosition[] = { eyePos.x(), eyePos.y(), eyePos.z() };
+ memcpy(p, ECCameraPosition, 12);
+ p += 16;
+
+ // Material
+ float ka[] = { 0.05f, 0.05f, 0.05f };
+ memcpy(p, ka, 12);
+ p += 16;
+
+ float kd[] = { 0.7f, 0.7f, 0.7f };
+ memcpy(p, kd, 12);
+ p += 16;
+
+ float ks[] = { 0.66f, 0.66f, 0.66f };
+ memcpy(p, ks, 12);
+ p += 16;
+
+ // Light parameters
+ float ECLightPosition[] = { m_lightPos.x(), m_lightPos.y(), m_lightPos.z() };
+ memcpy(p, ECLightPosition, 12);
+ p += 16;
+
+ float att[] = { 1, 0, 0 };
+ memcpy(p, att, 12);
+ p += 16;
+
+ float color[] = { 1.0f, 1.0f, 1.0f };
+ memcpy(p, color, 12);
+ p += 12; // next we have two floats which have an alignment of 4, hence 12 only
+
+ float intensity = 0.8f;
+ memcpy(p, &intensity, 4);
+ p += 4;
+
+ float specularExp = 150.0f;
+ memcpy(p, &specularExp, 4);
+ p += 4;
+}
+
+void Renderer::startNextFrame()
+{
+ // For demonstration purposes offload the command buffer generation onto a
+ // worker thread and continue with the frame submission only when it has
+ // finished.
+ Q_ASSERT(!m_framePending);
+ m_framePending = true;
+ QFuture<void> future = QtConcurrent::run(this, &Renderer::buildFrame);
+ m_frameWatcher.setFuture(future);
+}
+
+void Renderer::buildFrame()
+{
+ QMutexLocker locker(&m_guiMutex);
+
+ ensureBuffers();
+ ensureInstanceBuffer();
+ m_pipelinesFuture.waitForFinished();
+
+ VkCommandBuffer cb = m_window->currentCommandBuffer();
+ const QSize sz = m_window->swapChainImageSize();
+
+ VkClearColorValue clearColor = { 0.67f, 0.84f, 0.9f, 1.0f };
+ 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);
+
+ VkViewport viewport = {
+ 0, 0,
+ float(sz.width()), float(sz.height()),
+ 0, 1
+ };
+ m_devFuncs->vkCmdSetViewport(cb, 0, 1, &viewport);
+
+ VkRect2D scissor = {
+ { 0, 0 },
+ { uint32_t(sz.width()), uint32_t(sz.height()) }
+ };
+ m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor);
+
+ buildDrawCallsForFloor();
+ buildDrawCallsForItems();
+
+ m_devFuncs->vkCmdEndRenderPass(cmdBuf);
+}
+
+void Renderer::buildDrawCallsForItems()
+{
+ VkDevice dev = m_window->device();
+ VkCommandBuffer cb = m_window->currentCommandBuffer();
+
+ m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_itemMaterial.pipeline);
+
+ VkDeviceSize vbOffset = 0;
+ m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, m_useLogo ? &m_logoVertexBuf : &m_blockVertexBuf, &vbOffset);
+ m_devFuncs->vkCmdBindVertexBuffers(cb, 1, 1, &m_instBuf, &vbOffset);
+
+ // Now provide offsets so that the two dynamic buffers point to the
+ // beginning of the vertex and fragment uniform data for the current frame.
+ uint32_t frameUniOffset = m_window->currentFrame() * (m_itemMaterial.vertUniSize + m_itemMaterial.fragUniSize);
+ uint32_t frameUniOffsets[] = { frameUniOffset, frameUniOffset };
+ m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_itemMaterial.pipelineLayout, 0, 1,
+ &m_itemMaterial.descSet, 2, frameUniOffsets);
+
+ if (m_animating)
+ m_rotation += 0.5;
+
+ if (m_animating || m_vpDirty) {
+ if (m_vpDirty)
+ --m_vpDirty;
+ QMatrix4x4 vp, model;
+ QMatrix3x3 modelNormal;
+ QVector3D eyePos;
+ getMatrices(&vp, &model, &modelNormal, &eyePos);
+
+ // Map the uniform data for the current frame, ignore the geometry data at
+ // the beginning and the uniforms for other frames.
+ quint8 *p;
+ VkResult err = m_devFuncs->vkMapMemory(dev, m_bufMem,
+ m_itemMaterial.uniMemStartOffset + frameUniOffset,
+ m_itemMaterial.vertUniSize + m_itemMaterial.fragUniSize,
+ 0, reinterpret_cast<void **>(&p));
+ if (err != VK_SUCCESS)
+ qFatal("Failed to map memory: %d", err);
+
+ // Vertex shader uniforms
+ memcpy(p, vp.constData(), 64);
+ memcpy(p + 64, model.constData(), 64);
+ const float *mnp = modelNormal.constData();
+ memcpy(p + 128, mnp, 12);
+ memcpy(p + 128 + 16, mnp + 3, 12);
+ memcpy(p + 128 + 32, mnp + 6, 12);
+
+ // Fragment shader uniforms
+ p += m_itemMaterial.vertUniSize;
+ writeFragUni(p, eyePos);
+
+ m_devFuncs->vkUnmapMemory(dev, m_bufMem);
+ }
+
+ m_devFuncs->vkCmdDraw(cb, (m_useLogo ? m_logoMesh.data() : m_blockMesh.data())->vertexCount, m_instCount, 0, 0);
+}
+
+void Renderer::buildDrawCallsForFloor()
+{
+ VkCommandBuffer cb = m_window->currentCommandBuffer();
+
+ m_devFuncs->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_floorMaterial.pipeline);
+
+ VkDeviceSize vbOffset = 0;
+ m_devFuncs->vkCmdBindVertexBuffers(cb, 0, 1, &m_floorVertexBuf, &vbOffset);
+
+ QMatrix4x4 mvp = m_proj * m_cam.viewMatrix() * m_floorModel;
+ m_devFuncs->vkCmdPushConstants(cb, m_floorMaterial.pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, 64, mvp.constData());
+ float color[] = { 0.67f, 1.0f, 0.2f };
+ m_devFuncs->vkCmdPushConstants(cb, m_floorMaterial.pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 64, 12, color);
+
+ m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0);
+}
+
+void Renderer::addNew()
+{
+ QMutexLocker locker(&m_guiMutex);
+ m_instCount = qMin(m_instCount + 16, MAX_INSTANCES);
+}
+
+void Renderer::yaw(float degrees)
+{
+ QMutexLocker locker(&m_guiMutex);
+ m_cam.yaw(degrees);
+ markViewProjDirty();
+}
+
+void Renderer::pitch(float degrees)
+{
+ QMutexLocker locker(&m_guiMutex);
+ m_cam.pitch(degrees);
+ markViewProjDirty();
+}
+
+void Renderer::walk(float amount)
+{
+ QMutexLocker locker(&m_guiMutex);
+ m_cam.walk(amount);
+ markViewProjDirty();
+}
+
+void Renderer::strafe(float amount)
+{
+ QMutexLocker locker(&m_guiMutex);
+ m_cam.strafe(amount);
+ markViewProjDirty();
+}
+
+void Renderer::setUseLogo(bool b)
+{
+ QMutexLocker locker(&m_guiMutex);
+ m_useLogo = b;
+ if (!m_animating)
+ m_window->requestUpdate();
+}
diff --git a/examples/vulkan/hellovulkancubes/renderer.h b/examples/vulkan/hellovulkancubes/renderer.h
new file mode 100644
index 0000000000..60bb48377e
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/renderer.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef RENDERER_H
+#define RENDERER_H
+
+#include "vulkanwindow.h"
+#include "mesh.h"
+#include "shader.h"
+#include "camera.h"
+#include <QFutureWatcher>
+#include <QMutex>
+
+class Renderer : public QVulkanWindowRenderer
+{
+public:
+ Renderer(VulkanWindow *w, int initialCount);
+
+ void preInitResources() override;
+ void initResources() override;
+ void initSwapChainResources() override;
+ void releaseSwapChainResources() override;
+ void releaseResources() override;
+
+ void startNextFrame() override;
+
+ bool animating() const { return m_animating; }
+ void setAnimating(bool a) { m_animating = a; }
+
+ int instanceCount() const { return m_instCount; }
+ void addNew();
+
+ void yaw(float degrees);
+ void pitch(float degrees);
+ void walk(float amount);
+ void strafe(float amount);
+
+ void setUseLogo(bool b);
+
+private:
+ void createPipelines();
+ void createItemPipeline();
+ void createFloorPipeline();
+ void ensureBuffers();
+ void ensureInstanceBuffer();
+ void getMatrices(QMatrix4x4 *mvp, QMatrix4x4 *model, QMatrix3x3 *modelNormal, QVector3D *eyePos);
+ void writeFragUni(quint8 *p, const QVector3D &eyePos);
+ void buildFrame();
+ void buildDrawCallsForItems();
+ void buildDrawCallsForFloor();
+
+ void markViewProjDirty() { m_vpDirty = m_window->concurrentFrameCount(); }
+
+ VulkanWindow *m_window;
+ QVulkanDeviceFunctions *m_devFuncs;
+
+ bool m_useLogo = false;
+ Mesh m_blockMesh;
+ Mesh m_logoMesh;
+ VkBuffer m_blockVertexBuf = VK_NULL_HANDLE;
+ VkBuffer m_logoVertexBuf = VK_NULL_HANDLE;
+ struct {
+ VkDeviceSize vertUniSize;
+ VkDeviceSize fragUniSize;
+ VkDeviceSize uniMemStartOffset;
+ Shader vs;
+ Shader fs;
+ VkDescriptorPool descPool = VK_NULL_HANDLE;
+ VkDescriptorSetLayout descSetLayout = VK_NULL_HANDLE;
+ VkDescriptorSet descSet;
+ VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
+ VkPipeline pipeline = VK_NULL_HANDLE;
+ } m_itemMaterial;
+
+ VkBuffer m_floorVertexBuf = VK_NULL_HANDLE;
+ struct {
+ Shader vs;
+ Shader fs;
+ VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
+ VkPipeline pipeline = VK_NULL_HANDLE;
+ } m_floorMaterial;
+
+ VkDeviceMemory m_bufMem = VK_NULL_HANDLE;
+ VkBuffer m_uniBuf = VK_NULL_HANDLE;
+
+ VkPipelineCache m_pipelineCache = VK_NULL_HANDLE;
+ QFuture<void> m_pipelinesFuture;
+
+ QVector3D m_lightPos;
+ Camera m_cam;
+
+ QMatrix4x4 m_proj;
+ int m_vpDirty = 0;
+ QMatrix4x4 m_floorModel;
+
+ bool m_animating;
+ float m_rotation = 0.0f;
+
+ int m_instCount;
+ int m_preparedInstCount = 0;
+ QByteArray m_instData;
+ VkBuffer m_instBuf = VK_NULL_HANDLE;
+ VkDeviceMemory m_instBufMem = VK_NULL_HANDLE;
+
+ QFutureWatcher<void> m_frameWatcher;
+ bool m_framePending;
+
+ QMutex m_guiMutex;
+};
+
+#endif
diff --git a/examples/vulkan/hellovulkancubes/shader.cpp b/examples/vulkan/hellovulkancubes/shader.cpp
new file mode 100644
index 0000000000..e1c01c6842
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/shader.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** 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 "shader.h"
+#include <QtConcurrentRun>
+#include <QFile>
+#include <QVulkanDeviceFunctions>
+
+void Shader::load(QVulkanInstance *inst, VkDevice dev, const QString &fn)
+{
+ reset();
+ m_maybeRunning = true;
+ m_future = QtConcurrent::run([inst, dev, fn]() {
+ ShaderData sd;
+ QFile f(fn);
+ if (!f.open(QIODevice::ReadOnly)) {
+ qWarning("Failed to open %s", qPrintable(fn));
+ return sd;
+ }
+ QByteArray blob = f.readAll();
+ 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());
+ VkResult err = inst->deviceFunctions(dev)->vkCreateShaderModule(dev, &shaderInfo, nullptr, &sd.shaderModule);
+ if (err != VK_SUCCESS) {
+ qWarning("Failed to create shader module: %d", err);
+ return sd;
+ }
+ return sd;
+ });
+}
+
+ShaderData *Shader::data()
+{
+ if (m_maybeRunning && !m_data.isValid())
+ m_data = m_future.result();
+
+ return &m_data;
+}
+
+void Shader::reset()
+{
+ *data() = ShaderData();
+ m_maybeRunning = false;
+}
diff --git a/examples/vulkan/hellovulkancubes/shader.h b/examples/vulkan/hellovulkancubes/shader.h
new file mode 100644
index 0000000000..265868d2b0
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/shader.h
@@ -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$
+**
+****************************************************************************/
+
+#ifndef SHADER_H
+#define SHADER_H
+
+#include <QVulkanInstance>
+#include <QFuture>
+
+struct ShaderData
+{
+ bool isValid() const { return shaderModule != VK_NULL_HANDLE; }
+ VkShaderModule shaderModule = VK_NULL_HANDLE;
+};
+
+class Shader
+{
+public:
+ void load(QVulkanInstance *inst, VkDevice dev, const QString &fn);
+ ShaderData *data();
+ bool isValid() { return data()->isValid(); }
+ void reset();
+
+private:
+ bool m_maybeRunning = false;
+ QFuture<ShaderData> m_future;
+ ShaderData m_data;
+};
+
+#endif
diff --git a/examples/vulkan/hellovulkancubes/vulkanwindow.cpp b/examples/vulkan/hellovulkancubes/vulkanwindow.cpp
new file mode 100644
index 0000000000..9a4eaf1901
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/vulkanwindow.cpp
@@ -0,0 +1,134 @@
+/****************************************************************************
+**
+** 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 "vulkanwindow.h"
+#include "renderer.h"
+#include <QMouseEvent>
+#include <QKeyEvent>
+
+VulkanWindow::VulkanWindow(bool dbg)
+ : m_debug(dbg)
+{
+}
+
+QVulkanWindowRenderer *VulkanWindow::createRenderer()
+{
+ m_renderer = new Renderer(this, 128);
+ return m_renderer;
+}
+
+void VulkanWindow::addNew()
+{
+ m_renderer->addNew();
+}
+
+void VulkanWindow::togglePaused()
+{
+ m_renderer->setAnimating(!m_renderer->animating());
+}
+
+void VulkanWindow::meshSwitched(bool enable)
+{
+ m_renderer->setUseLogo(enable);
+}
+
+void VulkanWindow::mousePressEvent(QMouseEvent *e)
+{
+ m_pressed = true;
+ m_lastPos = e->pos();
+}
+
+void VulkanWindow::mouseReleaseEvent(QMouseEvent *)
+{
+ m_pressed = false;
+}
+
+void VulkanWindow::mouseMoveEvent(QMouseEvent *e)
+{
+ if (!m_pressed)
+ return;
+
+ int dx = e->pos().x() - m_lastPos.x();
+ int dy = e->pos().y() - m_lastPos.y();
+
+ if (dy)
+ m_renderer->pitch(dy / 10.0f);
+
+ if (dx)
+ m_renderer->yaw(dx / 10.0f);
+
+ m_lastPos = e->pos();
+}
+
+void VulkanWindow::keyPressEvent(QKeyEvent *e)
+{
+ const float amount = e->modifiers().testFlag(Qt::ShiftModifier) ? 1.0f : 0.1f;
+ switch (e->key()) {
+ case Qt::Key_W:
+ m_renderer->walk(amount);
+ break;
+ case Qt::Key_S:
+ m_renderer->walk(-amount);
+ break;
+ case Qt::Key_A:
+ m_renderer->strafe(-amount);
+ break;
+ case Qt::Key_D:
+ m_renderer->strafe(amount);
+ break;
+ default:
+ break;
+ }
+}
+
+int VulkanWindow::instanceCount() const
+{
+ return m_renderer->instanceCount();
+}
diff --git a/examples/vulkan/hellovulkancubes/vulkanwindow.h b/examples/vulkan/hellovulkancubes/vulkanwindow.h
new file mode 100644
index 0000000000..d085c0bde7
--- /dev/null
+++ b/examples/vulkan/hellovulkancubes/vulkanwindow.h
@@ -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$
+**
+****************************************************************************/
+
+#ifndef VULKANWINDOW_H
+#define VULKANWINDOW_H
+
+#include <QVulkanWindow>
+
+class Renderer;
+
+class VulkanWindow : public QVulkanWindow
+{
+public:
+ VulkanWindow(bool dbg);
+
+ QVulkanWindowRenderer *createRenderer() override;
+
+ bool isDebugEnabled() const { return m_debug; }
+ int instanceCount() const;
+
+public slots:
+ void addNew();
+ void togglePaused();
+ void meshSwitched(bool enable);
+
+private:
+ void mousePressEvent(QMouseEvent *) override;
+ void mouseReleaseEvent(QMouseEvent *) override;
+ void mouseMoveEvent(QMouseEvent *) override;
+ void keyPressEvent(QKeyEvent *) override;
+
+ bool m_debug;
+ Renderer *m_renderer;
+ bool m_pressed = false;
+ QPoint m_lastPos;
+};
+
+#endif