qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_example(hellovulkantriangle)
if(TARGET Qt6::Widgets)
qt_internal_add_example(hellovulkanwidget)
endif()
if(TARGET Qt6::Concurrent AND TARGET Qt6::Widgets)
qt_internal_add_example(hellovulkancubes)
endif()

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@ -0,0 +1,35 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example hellovulkancubes
\meta installpath vulkan
\title Hello Vulkan Cubes Example
\ingroup examples-vulkan
\brief Shows the basics of using QVulkanWindow.
The \e{Hello Vulkan Cubes Example} shows more advanced usage of QVulkanWindow.
\image hellovulkancubes.png
In this example there is a mesh loaded from a file and two different
materials and corresponding graphics pipelines. The rounded cubes are drawn
using instancing and feature a Phong lighting model with a single
directional light.
Unlike hellovulkantexture and hellovulkantriangle, the uniform buffer
handling takes an alternative approach here: dynamic uniform buffers are
used instead of multiple descriptor sets.
The example requires QtConcurrent since it demonstrates simple usage of
QtConcurrent::run(), QFuture, and QFutureWatcher in combination of
QVulkanWindow. Mesh and shader data loading, the potentially expensive
graphics pipeline construction, and the building of the frame command buffer
are all done in separate worker threads.
The scene is embedded into a widget-based user interface. The QVulkanWindow
subclass handles mouse and keyboard input as well since it provides a
first-person style camera in order to allow moving around in the scene.
\include examples-run.qdocinc
*/

View File

@ -0,0 +1,68 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example hellovulkantriangle
\meta installpath vulkan
\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} creates a full graphics pipeline,
including a vertex and fragment shader, to render a triangle.
\image hellovulkantriangle.png
\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 hellovulkantriangle/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 hellovulkantriangle/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.
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 hellovulkantriangle/main.cpp 2
\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(). To get continuous updates, the example simply invokes
QWindow::requestUpdate() in order to schedule a repaint.
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,26 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example hellovulkanwidget
\meta installpath vulkan
\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,68 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(hellovulkancubes LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/vulkan/hellovulkancubes")
find_package(Qt6 REQUIRED COMPONENTS Concurrent Core Gui Widgets)
qt_standard_project_setup()
qt_add_executable(hellovulkancubes
camera.cpp camera.h
main.cpp
mainwindow.cpp mainwindow.h
mesh.cpp mesh.h
renderer.cpp renderer.h
shader.cpp shader.h
vulkanwindow.cpp vulkanwindow.h
)
set_target_properties(hellovulkancubes PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(hellovulkancubes PRIVATE
Qt6::Concurrent
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
# Resources:
set_source_files_properties("../shared/block.buf"
PROPERTIES QT_RESOURCE_ALIAS "block.buf"
)
set_source_files_properties("../shared/qt_logo.buf"
PROPERTIES QT_RESOURCE_ALIAS "qt_logo.buf"
)
set(hellovulkancubes_resource_files
"../shared/block.buf"
"../shared/qt_logo.buf"
"color_frag.spv"
"color_phong_frag.spv"
"color_phong_vert.spv"
"color_vert.spv"
)
qt6_add_resources(hellovulkancubes "hellovulkancubes"
PREFIX
"/"
FILES
${hellovulkancubes_resource_files}
)
install(TARGETS hellovulkancubes
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,65 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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;
}

View File

@ -0,0 +1,33 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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

View File

@ -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);
}

View File

@ -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;
}

Binary file not shown.

View File

@ -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);
}

View File

@ -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;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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

View File

@ -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>

View File

@ -0,0 +1,33 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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"));
inst.setLayers({ "VK_LAYER_KHRONOS_validation" });
}
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();
}

View File

@ -0,0 +1,70 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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;
}

View File

@ -0,0 +1,36 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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

View File

@ -0,0 +1,51 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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;
}

View File

@ -0,0 +1,32 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,111 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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

View File

@ -0,0 +1,47 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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;
}

View File

@ -0,0 +1,30 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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

View File

@ -0,0 +1,87 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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->position().toPoint();
}
void VulkanWindow::mouseReleaseEvent(QMouseEvent *)
{
m_pressed = false;
}
void VulkanWindow::mouseMoveEvent(QMouseEvent *e)
{
if (!m_pressed)
return;
int dx = e->position().toPoint().x() - m_lastPos.x();
int dy = e->position().toPoint().y() - m_lastPos.y();
if (dy)
m_renderer->pitch(dy / 10.0f);
if (dx)
m_renderer->yaw(dx / 10.0f);
m_lastPos = e->position().toPoint();
}
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();
}

View File

@ -0,0 +1,38 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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

View File

@ -0,0 +1,57 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(hellovulkantriangle LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/vulkan/hellovulkantriangle")
find_package(Qt6 REQUIRED COMPONENTS Core Gui)
qt_standard_project_setup()
qt_add_executable(hellovulkantriangle
../shared/trianglerenderer.cpp ../shared/trianglerenderer.h
main.cpp
)
set_target_properties(hellovulkantriangle PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(hellovulkantriangle PRIVATE
Qt6::Core
Qt6::Gui
)
# Resources:
set_source_files_properties("../shared/color_frag.spv"
PROPERTIES QT_RESOURCE_ALIAS "color_frag.spv"
)
set_source_files_properties("../shared/color_vert.spv"
PROPERTIES QT_RESOURCE_ALIAS "color_vert.spv"
)
set(hellovulkantriangle_resource_files
"../shared/color_frag.spv"
"../shared/color_vert.spv"
)
qt_add_resources(hellovulkantriangle "hellovulkantriangle"
PREFIX
"/"
FILES
${hellovulkantriangle_resource_files}
)
install(TARGETS hellovulkantriangle
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

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,46 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QGuiApplication>
#include <QVulkanInstance>
#include <QLoggingCategory>
#include "../shared/trianglerenderer.h"
Q_LOGGING_CATEGORY(lcVk, "qt.vulkan")
//! [2]
class VulkanWindow : public QVulkanWindow
{
public:
QVulkanWindowRenderer *createRenderer() override;
};
//! [2]
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"));
//! [0]
QVulkanInstance inst;
inst.setLayers({ "VK_LAYER_KHRONOS_validation" });
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,59 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(hellovulkanwidget LANGUAGES CXX)
if(NOT DEFINED INSTALL_EXAMPLESDIR)
set(INSTALL_EXAMPLESDIR "examples")
endif()
set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/vulkan/hellovulkanwidget")
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
qt_standard_project_setup()
qt_add_executable(hellovulkanwidget
../shared/trianglerenderer.cpp ../shared/trianglerenderer.h
hellovulkanwidget.cpp hellovulkanwidget.h
main.cpp
)
set_target_properties(hellovulkanwidget PROPERTIES
WIN32_EXECUTABLE TRUE
MACOSX_BUNDLE TRUE
)
target_link_libraries(hellovulkanwidget PRIVATE
Qt6::Core
Qt6::Gui
Qt6::Widgets
)
# Resources:
set_source_files_properties("../shared/color_frag.spv"
PROPERTIES QT_RESOURCE_ALIAS "color_frag.spv"
)
set_source_files_properties("../shared/color_vert.spv"
PROPERTIES QT_RESOURCE_ALIAS "color_vert.spv"
)
set(hellovulkanwidget_resource_files
"../shared/color_frag.spv"
"../shared/color_vert.spv"
)
qt_add_resources(hellovulkanwidget "hellovulkanwidget"
PREFIX
"/"
FILES
${hellovulkanwidget_resource_files}
)
install(TARGETS hellovulkanwidget
RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
)

View File

@ -0,0 +1,138 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "hellovulkanwidget.h"
#include <QVulkanFunctions>
#include <QApplication>
#include <QVBoxLayout>
#include <QPlainTextEdit>
#include <QPushButton>
#include <QLCDNumber>
#include <QFileDialog>
#include <QMessageBox>
#include <QTabWidget>
MainWindow::MainWindow(VulkanWindow *w, QPlainTextEdit *logWidget)
: m_window(w)
{
QWidget *wrapper = QWidget::createWindowContainer(w);
m_info = new QPlainTextEdit;
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;
m_infoTab = new QTabWidget(this);
m_infoTab->addTab(m_info, tr("Vulkan Info"));
m_infoTab->addTab(logWidget, tr("Debug Log"));
layout->addWidget(m_infoTab, 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->setPlainText(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::asprintf("Number of physical devices: %d\n", int(m_window->availablePhysicalDevices().count()));
QVulkanFunctions *f = inst->functions();
VkPhysicalDeviceProperties props;
f->vkGetPhysicalDeviceProperties(m_window->physicalDevice(), &props);
info += QString::asprintf("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::asprintf(" %s v%u\n", layer.name.constData(), layer.version);
info += QStringLiteral("Enabled instance layers:\n");
for (const QByteArray &layer : inst->layers())
info += QString::asprintf(" %s\n", layer.constData());
info += QStringLiteral("Supported instance extensions:\n");
for (const QVulkanExtension &ext : inst->supportedExtensions())
info += QString::asprintf(" %s v%u\n", ext.name.constData(), ext.version);
info += QStringLiteral("Enabled instance extensions:\n");
for (const QByteArray &ext : inst->extensions())
info += QString::asprintf(" %s\n", ext.constData());
info += QString::asprintf("Color format: %u\nDepth-stencil format: %u\n",
m_window->colorFormat(), m_window->depthStencilFormat());
info += QStringLiteral("Supported sample counts:");
const QList<int> sampleCounts = m_window->supportedSampleCounts();
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,58 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef HELLOVULKANWIDGET_H
#define HELLOVULKANWIDGET_H
#include "../shared/trianglerenderer.h"
#include <QWidget>
class VulkanWindow;
QT_BEGIN_NAMESPACE
class QTabWidget;
class QPlainTextEdit;
class QLCDNumber;
QT_END_NAMESPACE
class MainWindow : public QWidget
{
Q_OBJECT
public:
explicit MainWindow(VulkanWindow *w, QPlainTextEdit *logWidget);
public slots:
void onVulkanInfoReceived(const QString &text);
void onFrameQueued(int colorValue);
void onGrabRequested();
private:
VulkanWindow *m_window;
QTabWidget *m_infoTab;
QPlainTextEdit *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);
};
#endif // HELLOVULKANWIDGET_H

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,53 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include <QApplication>
#include <QPlainTextEdit>
#include <QVulkanInstance>
#include <QLibraryInfo>
#include <QLoggingCategory>
#include <QPointer>
#include "hellovulkanwidget.h"
Q_LOGGING_CATEGORY(lcVk, "qt.vulkan")
static QPointer<QPlainTextEdit> messageLogWidget;
static QtMessageHandler oldMessageHandler = nullptr;
static void messageHandler(QtMsgType msgType, const QMessageLogContext &logContext, const QString &text)
{
if (!messageLogWidget.isNull())
messageLogWidget->appendPlainText(text);
if (oldMessageHandler)
oldMessageHandler(msgType, logContext, text);
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
messageLogWidget = new QPlainTextEdit(QLatin1String(QLibraryInfo::build()) + QLatin1Char('\n'));
messageLogWidget->setReadOnly(true);
oldMessageHandler = qInstallMessageHandler(messageHandler);
QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true"));
QVulkanInstance inst;
inst.setLayers({ "VK_LAYER_KHRONOS_validation" });
if (!inst.create())
qFatal("Failed to create Vulkan instance: %d", inst.errorCode());
VulkanWindow *vulkanWindow = new VulkanWindow;
vulkanWindow->setVulkanInstance(&inst);
MainWindow mainWindow(vulkanWindow, messageLogWidget.data());
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();
}

Binary file not shown.

View File

@ -0,0 +1,100 @@
# Blender v2.78 (sub 0) OBJ File: ''
# www.blender.org
mtllib block.mtl
o Cube_Cube.001
v 0.450000 -0.500000 -0.450000
v 0.450000 -0.500000 0.450000
v -0.450000 -0.500000 0.450000
v -0.450000 -0.500000 -0.450000
v -0.500000 0.450000 0.450000
v -0.500000 0.450000 -0.450000
v -0.500000 -0.450000 -0.450000
v -0.500000 -0.450000 0.450000
v -0.450000 0.500000 -0.450000
v -0.450000 0.500000 0.450000
v 0.450000 0.500000 0.450000
v 0.450000 0.500000 -0.450000
v -0.450000 0.450000 -0.500000
v 0.450000 0.450000 -0.500000
v 0.450000 -0.450000 -0.500000
v -0.450000 -0.450000 -0.500000
v 0.450000 0.450000 0.500000
v -0.450000 0.450000 0.500000
v -0.450000 -0.450000 0.500000
v 0.450000 -0.450000 0.500000
v 0.500000 -0.450000 -0.450000
v 0.500000 0.450000 -0.450000
v 0.500000 -0.450000 0.450000
v 0.500000 0.450000 0.450000
vn 0.0000 -1.0000 -0.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn 0.0000 -0.0000 1.0000
vn 0.5774 -0.5773 -0.5774
vn 0.5774 0.5774 -0.5774
vn 0.5774 -0.5774 0.5774
vn 0.5774 0.5773 0.5774
vn -0.5774 -0.5773 -0.5774
vn -0.5773 0.5774 -0.5774
vn -0.5774 -0.5774 0.5774
vn -0.5774 0.5773 0.5774
vn 0.7071 0.0000 -0.7071
vn 0.7071 0.7071 0.0000
vn 0.7071 -0.0000 0.7071
vn 0.7071 -0.7071 -0.0000
vn 0.0000 0.7071 0.7071
vn -0.7071 -0.0000 0.7071
vn 0.0000 -0.7071 0.7071
vn -0.7071 0.7071 0.0000
vn -0.7071 0.0000 -0.7071
vn -0.7071 -0.7071 -0.0000
vn 0.0000 0.7071 -0.7071
vn 0.0000 -0.7071 -0.7071
vn 1.0000 0.0000 0.0000
usemtl None
s 1
f 2//1 4//1 1//1
f 6//2 8//2 5//2
f 10//3 12//3 9//3
f 14//4 16//4 13//4
f 18//5 20//5 17//5
f 15//6 21//6 1//6
f 14//7 12//7 22//7
f 20//8 2//8 23//8
f 11//9 17//9 24//9
f 16//10 4//10 7//10
f 9//11 13//11 6//11
f 8//12 3//12 19//12
f 10//13 5//13 18//13
f 14//14 21//14 15//14
f 11//15 22//15 12//15
f 20//16 24//16 17//16
f 1//17 23//17 2//17
f 10//18 17//18 11//18
f 8//19 18//19 5//19
f 2//20 19//20 3//20
f 9//21 5//21 10//21
f 16//22 6//22 13//22
f 3//23 7//23 4//23
f 12//24 13//24 9//24
f 4//25 15//25 1//25
f 24//26 21//26 22//26
f 2//1 3//1 4//1
f 6//2 7//2 8//2
f 10//3 11//3 12//3
f 14//4 15//4 16//4
f 18//5 19//5 20//5
f 14//14 22//14 21//14
f 11//15 24//15 22//15
f 20//16 23//16 24//16
f 1//17 21//17 23//17
f 10//18 18//18 17//18
f 8//19 19//19 18//19
f 2//20 20//20 19//20
f 9//21 6//21 5//21
f 16//22 7//22 6//22
f 3//23 8//23 7//23
f 12//24 14//24 13//24
f 4//25 16//25 15//25
f 24//26 23//26 21//26

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,194 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
var fs = require('fs');
var metadata = {
vertexCount: 0,
aabb: [[null, null], [null, null], [null, null]],
emitVertex: function(v) {
++metadata.vertexCount;
var aabb = metadata.aabb;
if (aabb[0][0] === null || v[0] < aabb[0][0]) // min x
aabb[0][0] = v[0];
if (aabb[0][1] === null || v[0] > aabb[0][1]) // max x
aabb[0][1] = v[0];
if (aabb[1][0] === null || v[1] < aabb[1][0]) // min y
aabb[1][0] = v[1];
if (aabb[1][1] === null || v[1] > aabb[1][1]) // max y
aabb[1][1] = v[1];
if (aabb[2][0] === null || v[2] < aabb[2][0]) // min z
aabb[2][0] = v[2];
if (aabb[2][1] === null || v[2] > aabb[2][1]) // max z
aabb[2][1] = v[2];
},
getBuffer: function() {
var aabb = metadata.aabb;
console.log(metadata.vertexCount + " vertices");
console.log("AABB: " + aabb[0][0] + ".." + aabb[0][1]
+ ", " + aabb[1][0] + ".." + aabb[1][1]
+ ", " + aabb[2][0] + ".." + aabb[2][1]);
var buf = new Buffer((2 + 6) * 4);
var format = 1, p = 0;
buf.writeUInt32LE(format, p++);
buf.writeUInt32LE(metadata.vertexCount, p++ * 4);
for (var i = 0; i < 3; ++i) {
buf.writeFloatLE(aabb[i][0], p++ * 4);
buf.writeFloatLE(aabb[i][1], p++ * 4);
}
return buf;
}
};
function makeVec(s, n) {
var v = [];
s.split(' ').forEach(function (coordStr) {
var coord = parseFloat(coordStr);
if (!isNaN(coord))
v.push(coord);
});
if (v.length != n) {
console.error("Wrong vector size, expected " + n + ", got " + v.length);
process.exit();
}
return v;
}
function parseObj(filename, callback) {
fs.readFile(filename, "ascii", function (err, data) {
if (err)
throw err;
var groupCount = 0;
var parsed = { 'vertices': [], 'normals': [], 'texcoords': [], 'links': [] };
var missingTexCount = 0, missingNormCount = 0;
data.split('\n').forEach(function (line) {
var s = line.trim();
if (!s.length || groupCount > 1)
return;
if (s[0] === '#')
return;
if (s[0] === 'g') {
++groupCount;
} else if (s.substr(0, 2) === "v ") {
parsed.vertices.push(makeVec(s, 3));
} else if (s.substr(0, 3) === "vn ") {
parsed.normals.push(makeVec(s, 3));
} else if (s.substr(0, 3) === "vt ") {
parsed.texcoords.push(makeVec(s, 2));
} else if (s.substr(0, 2) === "f ") {
var refs = s.split(' ');
var vertCount = refs.length - 1;
if (vertCount != 3)
console.warn("Face " + parsed.links.length / 3 + " has " + vertCount + " vertices! (not triangulated?)");
for (var i = 1, ie = Math.min(4, refs.length); i < ie; ++i) {
var refComps = refs[i].split('/');
var vertIndex = parseInt(refComps[0]) - 1;
var texIndex = -1;
if (refComps.length >= 2 && refComps[1].length)
texIndex = parseInt(refComps[1]) - 1;
var normIndex = -1;
if (refComps.length >= 3 && refComps[2].length)
normIndex = parseInt(refComps[2]) - 1;
parsed.links.push([vertIndex, texIndex, normIndex]);
if (texIndex == -1)
++missingTexCount;
if (normIndex == -1)
++missingNormCount;
}
}
});
console.log(missingTexCount + " missing texture coordinates, " + missingNormCount + " missing normals");
callback(parsed);
});
}
function fillVert(src, index, dst, elemCount, isVertexCoord) {
var vertex = [];
if (index >= 0) {
for (var i = 0; i < elemCount; ++i) {
var elem = src[index][i];
if (isVertexCoord)
vertex.push(elem);
dst.buf.writeFloatLE(elem, dst.bufptr++ * 4);
}
if (vertex.length == 3)
metadata.emitVertex(vertex);
} else {
if (isVertexCoord) {
console.error("Missing vertex");
process.exit();
}
for (var i = 0; i < elemCount; ++i)
dst.buf.writeFloatLE(0, dst.bufptr++ * 4);
}
return vertex;
}
function normalize(v) {
var len = v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
if (len == 0.0 || len == 1.0)
return;
len = Math.sqrt(len);
return [ v[0] / len, v[1] / len, v[2] / len ];
}
function surfaceNormal(a, b, c) {
var u = [ b[0] - a[0], b[1] - a[1], b[2] - a[2] ];
var v = [ c[0] - a[0], c[1] - a[1], c[2] - a[2] ];
var result = [ u[1] * v[2] - u[2] * v[1],
u[2] * v[0] - u[0] * v[2],
u[0] * v[1] - u[1] * v[0] ];
return normalize(result);
}
function objDataToBuf(parsed) {
var floatCount = parsed.links.length * (3 + 2 + 3);
var buf = new Buffer(floatCount * 4);
var dst = { 'buf': buf, 'bufptr': 0 };
var tri = [];
var genNormals = false;
var genNormCount = 0;
for (var i = 0; i < parsed.links.length; ++i) {
var link = parsed.links[i];
var vertIndex = link[0], texIndex = link[1], normIndex = link[2];
tri.push(fillVert(parsed.vertices, vertIndex, dst, 3, true));
fillVert(parsed.texcoords, texIndex, dst, 2);
fillVert(parsed.normals, normIndex, dst, 3);
if (normIndex == -1)
genNormals = true;
if (tri.length == 3) {
if (genNormals) {
var norm = surfaceNormal(tri[0], tri[1], tri[2]);
for (var nvIdx = 0; nvIdx < 3; ++nvIdx) {
dst.buf.writeFloatLE(norm[0], (dst.bufptr - 3 - nvIdx * 8) * 4);
dst.buf.writeFloatLE(norm[1], (dst.bufptr - 2 - nvIdx * 8) * 4);
dst.buf.writeFloatLE(norm[2], (dst.bufptr - 1 - nvIdx * 8) * 4);
}
genNormCount += 3;
}
tri = [];
}
}
if (genNormCount)
console.log("Generated " + genNormCount + " normals");
return buf;
}
var inFilename = process.argv[2];
var outFilename = process.argv[3];
if (process.argv.length < 4) {
console.log("Usage: objconvert file.obj file.buf");
process.exit();
}
parseObj(inFilename, function (parsed) {
var buf = objDataToBuf(parsed);
var f = fs.createWriteStream(outFilename);
f.on("error", function (e) { console.error(e); });
f.write(metadata.getBuffer());
f.write(buf);
f.end();
console.log("Written to " + outFilename + ", format is:");
console.log(" uint32 version, uint32 vertex_count, float32 aabb[6], vertex_count * (float32 vertex[3], float32 texcoord[2], float32 normal[3])");
});

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,466 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#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[] = { // Y up, front = CCW
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) {
const QList<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,43 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef TRIANGLERENDERER_H
#define TRIANGLERENDERER_H
#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;
};
#endif // TRIANGLERENDERER_H

View File

@ -0,0 +1,8 @@
TEMPLATE = subdirs
SUBDIRS = hellovulkantriangle
qtHaveModule(widgets) {
SUBDIRS += hellovulkanwidget
qtHaveModule(concurrent): SUBDIRS += hellovulkancubes
}