Vulkan with simple sprite
This commit is contained in:
@@ -166,4 +166,6 @@ RESOURCES += \
|
|||||||
|
|
||||||
OTHER_FILES += \
|
OTHER_FILES += \
|
||||||
src/engine/vulkan_shader_modules/color.frag \
|
src/engine/vulkan_shader_modules/color.frag \
|
||||||
src/engine/vulkan_shader_modules/color.vert
|
src/engine/vulkan_shader_modules/color.vert \
|
||||||
|
src/engine/vulkan_shader_modules/texture.frag \
|
||||||
|
src/engine/vulkan_shader_modules/texture.vert
|
||||||
|
@@ -2,14 +2,16 @@
|
|||||||
|
|
||||||
#include <QVulkanInstance>
|
#include <QVulkanInstance>
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include "closeeventfilter.h"
|
#include "closeeventfilter.h"
|
||||||
|
|
||||||
GameEngine::GameEngine(const ProjectContainer &project, QObject *parent) :
|
GameEngine::GameEngine(const ProjectContainer &project, QObject *parent) :
|
||||||
QObject{parent},
|
QObject{parent},
|
||||||
m_project{project},
|
m_project{project},
|
||||||
m_glGameWindow{m_project},
|
m_glGameWindow{m_project, m_rotation},
|
||||||
m_vulkanGameWindow{m_project}
|
m_vulkanGameWindow{m_project, m_rotation},
|
||||||
|
m_timerId(startTimer(1000/60, Qt::PreciseTimer))
|
||||||
{
|
{
|
||||||
m_vulkanInstance.setLayers(QByteArrayList { "VK_LAYER_LUNARG_standard_validation" });
|
m_vulkanInstance.setLayers(QByteArrayList { "VK_LAYER_LUNARG_standard_validation" });
|
||||||
|
|
||||||
@@ -17,6 +19,9 @@ GameEngine::GameEngine(const ProjectContainer &project, QObject *parent) :
|
|||||||
qFatal("Failed to create Vulkan instance: %d", m_vulkanInstance.errorCode());
|
qFatal("Failed to create Vulkan instance: %d", m_vulkanInstance.errorCode());
|
||||||
|
|
||||||
m_vulkanGameWindow.setVulkanInstance(&m_vulkanInstance);
|
m_vulkanGameWindow.setVulkanInstance(&m_vulkanInstance);
|
||||||
|
|
||||||
|
m_glGameWindow.setModality(Qt::WindowModal);
|
||||||
|
m_vulkanGameWindow.setModality(Qt::WindowModal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameEngine::run()
|
void GameEngine::run()
|
||||||
@@ -36,5 +41,28 @@ void GameEngine::run()
|
|||||||
m_glGameWindow.show();
|
m_glGameWindow.show();
|
||||||
m_vulkanGameWindow.show();
|
m_vulkanGameWindow.show();
|
||||||
|
|
||||||
|
QTimer::singleShot(100, &m_glGameWindow, [this](){
|
||||||
|
auto geometry = m_glGameWindow.geometry();
|
||||||
|
qDebug() << geometry;
|
||||||
|
geometry.moveLeft(geometry.left() - (geometry.width() / 2));
|
||||||
|
m_glGameWindow.setGeometry(geometry);
|
||||||
|
});
|
||||||
|
QTimer::singleShot(100, &m_vulkanGameWindow, [this](){
|
||||||
|
auto geometry = m_vulkanGameWindow.geometry();
|
||||||
|
qDebug() << geometry;
|
||||||
|
geometry.moveLeft(geometry.left() + (geometry.width() / 2));
|
||||||
|
m_vulkanGameWindow.setGeometry(geometry);
|
||||||
|
});
|
||||||
|
|
||||||
eventLoop.exec();
|
eventLoop.exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameEngine::timerEvent(QTimerEvent *event)
|
||||||
|
{
|
||||||
|
if (event->timerId() == m_timerId)
|
||||||
|
{
|
||||||
|
m_rotation += 1.f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
QObject::timerEvent(event);
|
||||||
|
}
|
||||||
|
@@ -16,11 +16,18 @@ public:
|
|||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void timerEvent(QTimerEvent *event) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const ProjectContainer &m_project;
|
const ProjectContainer &m_project;
|
||||||
|
|
||||||
QVulkanInstance m_vulkanInstance;
|
QVulkanInstance m_vulkanInstance;
|
||||||
|
|
||||||
|
float m_rotation{};
|
||||||
|
|
||||||
GlGameWindow m_glGameWindow;
|
GlGameWindow m_glGameWindow;
|
||||||
VulkanGameWindow m_vulkanGameWindow;
|
VulkanGameWindow m_vulkanGameWindow;
|
||||||
|
|
||||||
|
const int m_timerId;
|
||||||
};
|
};
|
||||||
|
@@ -8,9 +8,10 @@
|
|||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
|
||||||
GlGameWindow::GlGameWindow(const ProjectContainer &project, QWindow *parent) :
|
GlGameWindow::GlGameWindow(const ProjectContainer &project, const float &rotation, QWindow *parent) :
|
||||||
QWindow{parent},
|
QWindow{parent},
|
||||||
m_project{project}
|
m_project{project},
|
||||||
|
m_rotation{rotation}
|
||||||
{
|
{
|
||||||
setSurfaceType(QWindow::OpenGLSurface);
|
setSurfaceType(QWindow::OpenGLSurface);
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ GlGameWindow::GlGameWindow(const ProjectContainer &project, QWindow *parent) :
|
|||||||
setMaximumWidth(640);
|
setMaximumWidth(640);
|
||||||
setMinimumHeight(480);
|
setMinimumHeight(480);
|
||||||
setMaximumHeight(480);
|
setMaximumHeight(480);
|
||||||
|
resize(640, 480);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlGameWindow::~GlGameWindow() = default;
|
GlGameWindow::~GlGameWindow() = default;
|
||||||
@@ -54,7 +56,7 @@ void GlGameWindow::render()
|
|||||||
QMatrix4x4 matrix;
|
QMatrix4x4 matrix;
|
||||||
matrix.perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f);
|
matrix.perspective(60.0f, 4.0f / 3.0f, 0.1f, 100.0f);
|
||||||
matrix.translate(0, 0, -2);
|
matrix.translate(0, 0, -2);
|
||||||
matrix.rotate(100.0f * m_frame / screen()->refreshRate(), 0, 1, 0);
|
matrix.rotate(m_rotation, 0, 1, 0);
|
||||||
|
|
||||||
m_program->setUniformValue(m_matrixUniform, matrix);
|
m_program->setUniformValue(m_matrixUniform, matrix);
|
||||||
|
|
||||||
@@ -82,8 +84,6 @@ void GlGameWindow::render()
|
|||||||
glDisableVertexAttribArray(m_posAttr);
|
glDisableVertexAttribArray(m_posAttr);
|
||||||
|
|
||||||
m_program->release();
|
m_program->release();
|
||||||
|
|
||||||
++m_frame;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GlGameWindow::renderLater()
|
void GlGameWindow::renderLater()
|
||||||
|
@@ -13,7 +13,7 @@ class GlGameWindow : public QWindow, protected QOpenGLFunctions
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit GlGameWindow(const ProjectContainer &project, QWindow *parent = nullptr);
|
explicit GlGameWindow(const ProjectContainer &project, const float &m_rotation, QWindow *parent = nullptr);
|
||||||
~GlGameWindow();
|
~GlGameWindow();
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
@@ -41,5 +41,5 @@ private:
|
|||||||
GLint m_matrixUniform = 0;
|
GLint m_matrixUniform = 0;
|
||||||
|
|
||||||
QOpenGLShaderProgram *m_program{};
|
QOpenGLShaderProgram *m_program{};
|
||||||
int m_frame{};
|
const float &m_rotation;
|
||||||
};
|
};
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
<RCC>
|
<RCC>
|
||||||
<qresource prefix="/qtgameengine">
|
<qresource prefix="/qtgameengine">
|
||||||
<file>vulkan_shader_modules/color_frag.spv</file>
|
|
||||||
<file>vulkan_shader_modules/color_vert.spv</file>
|
|
||||||
<file>opengl_shaders/color.frag</file>
|
<file>opengl_shaders/color.frag</file>
|
||||||
<file>opengl_shaders/color.vert</file>
|
<file>opengl_shaders/color.vert</file>
|
||||||
|
<file>vulkan_shader_modules/color_frag.spv</file>
|
||||||
|
<file>vulkan_shader_modules/color_vert.spv</file>
|
||||||
|
<file>vulkan_shader_modules/texture_frag.spv</file>
|
||||||
|
<file>vulkan_shader_modules/texture_vert.spv</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
12
src/engine/vulkan_shader_modules/texture.frag
Normal file
12
src/engine/vulkan_shader_modules/texture.frag
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#version 440
|
||||||
|
|
||||||
|
layout(location = 0) in vec2 v_texcoord;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
|
||||||
|
layout(binding = 1) uniform sampler2D tex;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
fragColor = texture(tex, v_texcoord);
|
||||||
|
}
|
18
src/engine/vulkan_shader_modules/texture.vert
Normal file
18
src/engine/vulkan_shader_modules/texture.vert
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#version 440
|
||||||
|
|
||||||
|
layout(location = 0) in vec4 position;
|
||||||
|
layout(location = 1) in vec2 texcoord;
|
||||||
|
|
||||||
|
layout(location = 0) out vec2 v_texcoord;
|
||||||
|
|
||||||
|
layout(std140, binding = 0) uniform buf {
|
||||||
|
mat4 mvp;
|
||||||
|
} ubuf;
|
||||||
|
|
||||||
|
out gl_PerVertex { vec4 gl_Position; };
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
v_texcoord = texcoord;
|
||||||
|
gl_Position = ubuf.mvp * position;
|
||||||
|
}
|
BIN
src/engine/vulkan_shader_modules/texture_frag.spv
Normal file
BIN
src/engine/vulkan_shader_modules/texture_frag.spv
Normal file
Binary file not shown.
BIN
src/engine/vulkan_shader_modules/texture_vert.spv
Normal file
BIN
src/engine/vulkan_shader_modules/texture_vert.spv
Normal file
Binary file not shown.
@@ -3,14 +3,18 @@
|
|||||||
#include <QVulkanFunctions>
|
#include <QVulkanFunctions>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "projectcontainer.h"
|
||||||
|
|
||||||
// Note that the vertex data and the projection matrix assume OpenGL. With
|
// 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
|
// 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
|
// of -1/1. These will be corrected for by an extra transformation when
|
||||||
// calculating the modelview-projection matrix.
|
// calculating the modelview-projection matrix.
|
||||||
static float vertexData[] = { // Y up, front = CCW
|
static float vertexData[] = { // Y up, front = CW
|
||||||
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
|
// x, y, z, u, v
|
||||||
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
|
-1, -1, 0, 0, 1,
|
||||||
0.5f, -0.5f, 0.0f, 0.0f, 1.0f
|
-1, 1, 0, 0, 0,
|
||||||
|
1, -1, 0, 1, 1,
|
||||||
|
1, 1, 0, 1, 0
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int UNIFORM_DATA_SIZE = 16 * sizeof(float);
|
static const int UNIFORM_DATA_SIZE = 16 * sizeof(float);
|
||||||
@@ -20,9 +24,10 @@ static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
|
|||||||
return (v + byteAlign - 1) & ~(byteAlign - 1);
|
return (v + byteAlign - 1) & ~(byteAlign - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanGameRenderer::VulkanGameRenderer(const ProjectContainer &project, QVulkanWindow *w, bool msaa) :
|
VulkanGameRenderer::VulkanGameRenderer(const ProjectContainer &project, const float &rotation, QVulkanWindow *w, bool msaa) :
|
||||||
m_window{w},
|
m_window{w},
|
||||||
m_project{project}
|
m_project{project},
|
||||||
|
m_rotation{rotation}
|
||||||
{
|
{
|
||||||
if (msaa)
|
if (msaa)
|
||||||
{
|
{
|
||||||
@@ -69,6 +74,281 @@ VkShaderModule VulkanGameRenderer::createShader(const QString &name)
|
|||||||
return shaderModule;
|
return shaderModule;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool VulkanGameRenderer::createTextureFromFile(const QString &name)
|
||||||
|
{
|
||||||
|
QImage img{name};
|
||||||
|
if (img.isNull())
|
||||||
|
{
|
||||||
|
qWarning("Failed to load image %s", qPrintable(name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createTexture(img);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanGameRenderer::createTexture(const QImage &image)
|
||||||
|
{
|
||||||
|
// Convert to byte ordered RGBA8. Use premultiplied alpha, see pColorBlendState in the pipeline.
|
||||||
|
const auto img = image.convertToFormat(QImage::Format_RGBA8888_Premultiplied);
|
||||||
|
|
||||||
|
QVulkanFunctions *f = m_window->vulkanInstance()->functions();
|
||||||
|
VkDevice dev = m_window->device();
|
||||||
|
|
||||||
|
const bool srgb = false; //QCoreApplication::arguments().contains(QStringLiteral("--srgb"));
|
||||||
|
if (srgb)
|
||||||
|
qDebug("sRGB swapchain was requested, making texture sRGB too");
|
||||||
|
|
||||||
|
m_texFormat = srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
|
|
||||||
|
// Now we can either map and copy the image data directly, or have to go
|
||||||
|
// through a staging buffer to copy and convert into the internal optimal
|
||||||
|
// tiling format.
|
||||||
|
VkFormatProperties props;
|
||||||
|
f->vkGetPhysicalDeviceFormatProperties(m_window->physicalDevice(), m_texFormat, &props);
|
||||||
|
const bool canSampleLinear = (props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
|
||||||
|
const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
|
||||||
|
if (!canSampleLinear && !canSampleOptimal)
|
||||||
|
{
|
||||||
|
qWarning("Neither linear nor optimal image sampling is supported for RGBA8");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool alwaysStage = qEnvironmentVariableIntValue("QT_VK_FORCE_STAGE_TEX");
|
||||||
|
|
||||||
|
if (canSampleLinear && !alwaysStage)
|
||||||
|
{
|
||||||
|
if (!createTextureImage(img.size(), &m_texImage, &m_texMem,
|
||||||
|
VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||||
|
m_window->hostVisibleMemoryIndex()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!writeLinearImage(img, m_texImage, m_texMem))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_texLayoutPending = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!createTextureImage(img.size(), &m_texStaging, &m_texStagingMem,
|
||||||
|
VK_IMAGE_TILING_LINEAR, VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
|
||||||
|
m_window->hostVisibleMemoryIndex()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!createTextureImage(img.size(), &m_texImage, &m_texMem,
|
||||||
|
VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||||
|
m_window->deviceLocalMemoryIndex()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!writeLinearImage(img, m_texStaging, m_texStagingMem))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_texStagingPending = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkImageViewCreateInfo viewInfo;
|
||||||
|
memset(&viewInfo, 0, sizeof(viewInfo));
|
||||||
|
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
|
viewInfo.image = m_texImage;
|
||||||
|
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
viewInfo.format = m_texFormat;
|
||||||
|
viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
|
||||||
|
viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
|
||||||
|
viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
|
||||||
|
viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
|
||||||
|
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
viewInfo.subresourceRange.levelCount = viewInfo.subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
VkResult err = m_devFuncs->vkCreateImageView(dev, &viewInfo, nullptr, &m_texView);
|
||||||
|
if (err != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
qWarning("Failed to create image view for texture: %d", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_texSize = img.size();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanGameRenderer::createTextureImage(const QSize &size, VkImage *image, VkDeviceMemory *mem,
|
||||||
|
VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex)
|
||||||
|
{
|
||||||
|
VkDevice dev = m_window->device();
|
||||||
|
|
||||||
|
VkImageCreateInfo imageInfo;
|
||||||
|
memset(&imageInfo, 0, sizeof(imageInfo));
|
||||||
|
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||||
|
imageInfo.imageType = VK_IMAGE_TYPE_2D;
|
||||||
|
imageInfo.format = m_texFormat;
|
||||||
|
imageInfo.extent.width = size.width();
|
||||||
|
imageInfo.extent.height = size.height();
|
||||||
|
imageInfo.extent.depth = 1;
|
||||||
|
imageInfo.mipLevels = 1;
|
||||||
|
imageInfo.arrayLayers = 1;
|
||||||
|
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
|
imageInfo.tiling = tiling;
|
||||||
|
imageInfo.usage = usage;
|
||||||
|
imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||||
|
|
||||||
|
VkResult err = m_devFuncs->vkCreateImage(dev, &imageInfo, nullptr, image);
|
||||||
|
if (err != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
qWarning("Failed to create linear image for texture: %d", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkMemoryRequirements memReq;
|
||||||
|
m_devFuncs->vkGetImageMemoryRequirements(dev, *image, &memReq);
|
||||||
|
|
||||||
|
if (!(memReq.memoryTypeBits & (1 << memIndex)))
|
||||||
|
{
|
||||||
|
VkPhysicalDeviceMemoryProperties physDevMemProps;
|
||||||
|
m_window->vulkanInstance()->functions()->vkGetPhysicalDeviceMemoryProperties(m_window->physicalDevice(), &physDevMemProps);
|
||||||
|
for (uint32_t i = 0; i < physDevMemProps.memoryTypeCount; ++i)
|
||||||
|
{
|
||||||
|
if (!(memReq.memoryTypeBits & (1 << i)))
|
||||||
|
continue;
|
||||||
|
memIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VkMemoryAllocateInfo allocInfo {
|
||||||
|
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||||
|
nullptr,
|
||||||
|
memReq.size,
|
||||||
|
memIndex
|
||||||
|
};
|
||||||
|
qDebug("allocating %u bytes for texture image", uint32_t(memReq.size));
|
||||||
|
|
||||||
|
err = m_devFuncs->vkAllocateMemory(dev, &allocInfo, nullptr, mem);
|
||||||
|
if (err != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
qWarning("Failed to allocate memory for linear image: %d", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = m_devFuncs->vkBindImageMemory(dev, *image, *mem, 0);
|
||||||
|
if (err != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
qWarning("Failed to bind linear image memory: %d", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VulkanGameRenderer::writeLinearImage(const QImage &img, VkImage image, VkDeviceMemory memory)
|
||||||
|
{
|
||||||
|
VkDevice dev = m_window->device();
|
||||||
|
|
||||||
|
VkImageSubresource subres {
|
||||||
|
VK_IMAGE_ASPECT_COLOR_BIT,
|
||||||
|
0, // mip level
|
||||||
|
0
|
||||||
|
};
|
||||||
|
VkSubresourceLayout layout;
|
||||||
|
m_devFuncs->vkGetImageSubresourceLayout(dev, image, &subres, &layout);
|
||||||
|
|
||||||
|
uchar *p;
|
||||||
|
VkResult err = m_devFuncs->vkMapMemory(dev, memory, layout.offset, layout.size, 0, reinterpret_cast<void **>(&p));
|
||||||
|
if (err != VK_SUCCESS)
|
||||||
|
{
|
||||||
|
qWarning("Failed to map memory for linear image: %d", err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int y = 0; y < img.height(); ++y)
|
||||||
|
{
|
||||||
|
const uchar *line = img.constScanLine(y);
|
||||||
|
memcpy(p, line, img.width() * 4);
|
||||||
|
p += layout.rowPitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_devFuncs->vkUnmapMemory(dev, memory);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanGameRenderer::ensureTexture()
|
||||||
|
{
|
||||||
|
if (!m_texLayoutPending && !m_texStagingPending)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Q_ASSERT(m_texLayoutPending != m_texStagingPending);
|
||||||
|
VkCommandBuffer cb = m_window->currentCommandBuffer();
|
||||||
|
|
||||||
|
VkImageMemoryBarrier barrier;
|
||||||
|
memset(&barrier, 0, sizeof(barrier));
|
||||||
|
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||||
|
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
barrier.subresourceRange.levelCount = barrier.subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
if (m_texLayoutPending)
|
||||||
|
{
|
||||||
|
m_texLayoutPending = false;
|
||||||
|
|
||||||
|
barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||||
|
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||||||
|
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||||
|
barrier.image = m_texImage;
|
||||||
|
|
||||||
|
m_devFuncs->vkCmdPipelineBarrier(cb,
|
||||||
|
VK_PIPELINE_STAGE_HOST_BIT,
|
||||||
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||||
|
0, 0, nullptr, 0, nullptr,
|
||||||
|
1, &barrier);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_texStagingPending = false;
|
||||||
|
|
||||||
|
barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||||
|
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||||
|
barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
|
||||||
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||||
|
barrier.image = m_texStaging;
|
||||||
|
m_devFuncs->vkCmdPipelineBarrier(cb,
|
||||||
|
VK_PIPELINE_STAGE_HOST_BIT,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
0, 0, nullptr, 0, nullptr,
|
||||||
|
1, &barrier);
|
||||||
|
|
||||||
|
barrier.oldLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
|
||||||
|
barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
barrier.srcAccessMask = 0;
|
||||||
|
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
barrier.image = m_texImage;
|
||||||
|
m_devFuncs->vkCmdPipelineBarrier(cb,
|
||||||
|
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
0, 0, nullptr, 0, nullptr,
|
||||||
|
1, &barrier);
|
||||||
|
|
||||||
|
VkImageCopy copyInfo;
|
||||||
|
memset(©Info, 0, sizeof(copyInfo));
|
||||||
|
copyInfo.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
copyInfo.srcSubresource.layerCount = 1;
|
||||||
|
copyInfo.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
copyInfo.dstSubresource.layerCount = 1;
|
||||||
|
copyInfo.extent.width = m_texSize.width();
|
||||||
|
copyInfo.extent.height = m_texSize.height();
|
||||||
|
copyInfo.extent.depth = 1;
|
||||||
|
m_devFuncs->vkCmdCopyImage(cb, m_texStaging, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||||
|
m_texImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©Info);
|
||||||
|
|
||||||
|
barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||||
|
barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||||
|
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||||
|
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||||
|
barrier.image = m_texImage;
|
||||||
|
m_devFuncs->vkCmdPipelineBarrier(cb,
|
||||||
|
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||||||
|
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||||
|
0, 0, nullptr, 0, nullptr,
|
||||||
|
1, &barrier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void VulkanGameRenderer::initResources()
|
void VulkanGameRenderer::initResources()
|
||||||
{
|
{
|
||||||
qDebug("initResources");
|
qDebug("initResources");
|
||||||
@@ -153,14 +433,14 @@ void VulkanGameRenderer::initResources()
|
|||||||
{ // position
|
{ // position
|
||||||
0, // location
|
0, // location
|
||||||
0, // binding
|
0, // binding
|
||||||
VK_FORMAT_R32G32_SFLOAT,
|
VK_FORMAT_R32G32B32_SFLOAT,
|
||||||
0
|
0
|
||||||
},
|
},
|
||||||
{ // color
|
{ // texcoord
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
VK_FORMAT_R32G32B32_SFLOAT,
|
VK_FORMAT_R32G32_SFLOAT,
|
||||||
2 * sizeof(float)
|
3 * sizeof(float)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -173,31 +453,68 @@ void VulkanGameRenderer::initResources()
|
|||||||
vertexInputInfo.vertexAttributeDescriptionCount = 2;
|
vertexInputInfo.vertexAttributeDescriptionCount = 2;
|
||||||
vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc;
|
vertexInputInfo.pVertexAttributeDescriptions = vertexAttrDesc;
|
||||||
|
|
||||||
|
// Sampler.
|
||||||
|
VkSamplerCreateInfo samplerInfo;
|
||||||
|
memset(&samplerInfo, 0, sizeof(samplerInfo));
|
||||||
|
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||||
|
samplerInfo.magFilter = VK_FILTER_NEAREST;
|
||||||
|
samplerInfo.minFilter = VK_FILTER_NEAREST;
|
||||||
|
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||||
|
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||||
|
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||||
|
samplerInfo.maxAnisotropy = 1.0f;
|
||||||
|
err = m_devFuncs->vkCreateSampler(dev, &samplerInfo, nullptr, &m_sampler);
|
||||||
|
if (err != VK_SUCCESS)
|
||||||
|
qFatal("Failed to create sampler: %d", err);
|
||||||
|
|
||||||
|
if (m_project.sprites.empty() ||
|
||||||
|
m_project.sprites.front().pixmaps.empty() ||
|
||||||
|
m_project.sprites.front().pixmaps.front().isNull())
|
||||||
|
qFatal("no sprites in game project!");
|
||||||
|
|
||||||
|
// Texture.
|
||||||
|
//if (!createTextureFromFile(QStringLiteral(":/qtgameengine/qt256.png")))
|
||||||
|
// qFatal("Failed to create texture");
|
||||||
|
if (!createTexture(m_project.sprites.front().pixmaps.front().toImage()))
|
||||||
|
qFatal("Failed to create texture");
|
||||||
|
|
||||||
// Set up descriptor set and its layout.
|
// Set up descriptor set and its layout.
|
||||||
VkDescriptorPoolSize descPoolSizes = { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uint32_t(concurrentFrameCount) };
|
VkDescriptorPoolSize descPoolSizes[2] = {
|
||||||
|
{ VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, uint32_t(concurrentFrameCount) },
|
||||||
|
{ VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, uint32_t(concurrentFrameCount) }
|
||||||
|
};
|
||||||
VkDescriptorPoolCreateInfo descPoolInfo;
|
VkDescriptorPoolCreateInfo descPoolInfo;
|
||||||
memset(&descPoolInfo, 0, sizeof(descPoolInfo));
|
memset(&descPoolInfo, 0, sizeof(descPoolInfo));
|
||||||
descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||||
descPoolInfo.maxSets = concurrentFrameCount;
|
descPoolInfo.maxSets = concurrentFrameCount;
|
||||||
descPoolInfo.poolSizeCount = 1;
|
descPoolInfo.poolSizeCount = 2;
|
||||||
descPoolInfo.pPoolSizes = &descPoolSizes;
|
descPoolInfo.pPoolSizes = descPoolSizes;
|
||||||
err = m_devFuncs->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, &m_descPool);
|
err = m_devFuncs->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, &m_descPool);
|
||||||
if (err != VK_SUCCESS)
|
if (err != VK_SUCCESS)
|
||||||
qFatal("Failed to create descriptor pool: %d", err);
|
qFatal("Failed to create descriptor pool: %d", err);
|
||||||
|
|
||||||
VkDescriptorSetLayoutBinding layoutBinding {
|
VkDescriptorSetLayoutBinding layoutBinding[2] {
|
||||||
0, // binding
|
{
|
||||||
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
0, // binding
|
||||||
1,
|
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||||
VK_SHADER_STAGE_VERTEX_BIT,
|
1, // descriptorCount
|
||||||
nullptr
|
VK_SHADER_STAGE_VERTEX_BIT,
|
||||||
|
nullptr
|
||||||
|
},
|
||||||
|
{
|
||||||
|
1, // binding
|
||||||
|
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
|
1, // descriptorCount
|
||||||
|
VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||||
|
nullptr
|
||||||
|
}
|
||||||
};
|
};
|
||||||
VkDescriptorSetLayoutCreateInfo descLayoutInfo {
|
VkDescriptorSetLayoutCreateInfo descLayoutInfo {
|
||||||
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||||
nullptr,
|
nullptr,
|
||||||
0,
|
0,
|
||||||
1,
|
2, // bindingCount
|
||||||
&layoutBinding
|
layoutBinding
|
||||||
};
|
};
|
||||||
err = m_devFuncs->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_descSetLayout);
|
err = m_devFuncs->vkCreateDescriptorSetLayout(dev, &descLayoutInfo, nullptr, &m_descSetLayout);
|
||||||
if (err != VK_SUCCESS)
|
if (err != VK_SUCCESS)
|
||||||
@@ -216,14 +533,29 @@ void VulkanGameRenderer::initResources()
|
|||||||
if (err != VK_SUCCESS)
|
if (err != VK_SUCCESS)
|
||||||
qFatal("Failed to allocate descriptor set: %d", err);
|
qFatal("Failed to allocate descriptor set: %d", err);
|
||||||
|
|
||||||
VkWriteDescriptorSet descWrite;
|
VkWriteDescriptorSet descWrite[2];
|
||||||
memset(&descWrite, 0, sizeof(descWrite));
|
memset(descWrite, 0, sizeof(descWrite));
|
||||||
descWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
descWrite[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
descWrite.dstSet = m_descSet[i];
|
descWrite[0].dstSet = m_descSet[i];
|
||||||
descWrite.descriptorCount = 1;
|
descWrite[0].dstBinding = 0;
|
||||||
descWrite.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
descWrite[0].descriptorCount = 1;
|
||||||
descWrite.pBufferInfo = &m_uniformBufInfo[i];
|
descWrite[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
m_devFuncs->vkUpdateDescriptorSets(dev, 1, &descWrite, 0, nullptr);
|
descWrite[0].pBufferInfo = &m_uniformBufInfo[i];
|
||||||
|
|
||||||
|
VkDescriptorImageInfo descImageInfo {
|
||||||
|
m_sampler,
|
||||||
|
m_texView,
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
|
||||||
|
};
|
||||||
|
|
||||||
|
descWrite[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
descWrite[1].dstSet = m_descSet[i];
|
||||||
|
descWrite[1].dstBinding = 1;
|
||||||
|
descWrite[1].descriptorCount = 1;
|
||||||
|
descWrite[1].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
|
descWrite[1].pImageInfo = &descImageInfo;
|
||||||
|
|
||||||
|
m_devFuncs->vkUpdateDescriptorSets(dev, 2, descWrite, 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pipeline cache
|
// Pipeline cache
|
||||||
@@ -245,8 +577,8 @@ void VulkanGameRenderer::initResources()
|
|||||||
qFatal("Failed to create pipeline layout: %d", err);
|
qFatal("Failed to create pipeline layout: %d", err);
|
||||||
|
|
||||||
// Shaders
|
// Shaders
|
||||||
VkShaderModule vertShaderModule = createShader(QStringLiteral(":/qtgameengine/vulkan_shader_modules/color_vert.spv"));
|
VkShaderModule vertShaderModule = createShader(QStringLiteral(":/qtgameengine/vulkan_shader_modules/texture_vert.spv"));
|
||||||
VkShaderModule fragShaderModule = createShader(QStringLiteral(":/qtgameengine/vulkan_shader_modules/color_frag.spv"));
|
VkShaderModule fragShaderModule = createShader(QStringLiteral(":/qtgameengine/vulkan_shader_modules/texture_frag.spv"));
|
||||||
|
|
||||||
// Graphics pipeline
|
// Graphics pipeline
|
||||||
VkGraphicsPipelineCreateInfo pipelineInfo;
|
VkGraphicsPipelineCreateInfo pipelineInfo;
|
||||||
@@ -281,7 +613,7 @@ void VulkanGameRenderer::initResources()
|
|||||||
VkPipelineInputAssemblyStateCreateInfo ia;
|
VkPipelineInputAssemblyStateCreateInfo ia;
|
||||||
memset(&ia, 0, sizeof(ia));
|
memset(&ia, 0, sizeof(ia));
|
||||||
ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||||
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
|
||||||
pipelineInfo.pInputAssemblyState = &ia;
|
pipelineInfo.pInputAssemblyState = &ia;
|
||||||
|
|
||||||
// The viewport and scissor will be set dynamically via vkCmdSetViewport/Scissor.
|
// The viewport and scissor will be set dynamically via vkCmdSetViewport/Scissor.
|
||||||
@@ -297,16 +629,15 @@ void VulkanGameRenderer::initResources()
|
|||||||
memset(&rs, 0, sizeof(rs));
|
memset(&rs, 0, sizeof(rs));
|
||||||
rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||||
rs.polygonMode = VK_POLYGON_MODE_FILL;
|
rs.polygonMode = VK_POLYGON_MODE_FILL;
|
||||||
rs.cullMode = VK_CULL_MODE_NONE; // we want the back face as well
|
rs.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||||
rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
rs.frontFace = VK_FRONT_FACE_CLOCKWISE;
|
||||||
rs.lineWidth = 1.0f;
|
rs.lineWidth = 1.0f;
|
||||||
pipelineInfo.pRasterizationState = &rs;
|
pipelineInfo.pRasterizationState = &rs;
|
||||||
|
|
||||||
VkPipelineMultisampleStateCreateInfo ms;
|
VkPipelineMultisampleStateCreateInfo ms;
|
||||||
memset(&ms, 0, sizeof(ms));
|
memset(&ms, 0, sizeof(ms));
|
||||||
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||||
// Enable multisampling.
|
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
||||||
ms.rasterizationSamples = m_window->sampleCountFlagBits();
|
|
||||||
pipelineInfo.pMultisampleState = &ms;
|
pipelineInfo.pMultisampleState = &ms;
|
||||||
|
|
||||||
VkPipelineDepthStencilStateCreateInfo ds;
|
VkPipelineDepthStencilStateCreateInfo ds;
|
||||||
@@ -320,10 +651,17 @@ void VulkanGameRenderer::initResources()
|
|||||||
VkPipelineColorBlendStateCreateInfo cb;
|
VkPipelineColorBlendStateCreateInfo cb;
|
||||||
memset(&cb, 0, sizeof(cb));
|
memset(&cb, 0, sizeof(cb));
|
||||||
cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||||
// no blend, write out all of rgba
|
// assume pre-multiplied alpha, blend, write out all of rgba
|
||||||
VkPipelineColorBlendAttachmentState att;
|
VkPipelineColorBlendAttachmentState att;
|
||||||
memset(&att, 0, sizeof(att));
|
memset(&att, 0, sizeof(att));
|
||||||
att.colorWriteMask = 0xF;
|
att.colorWriteMask = 0xF;
|
||||||
|
att.blendEnable = VK_TRUE;
|
||||||
|
att.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||||
|
att.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||||
|
att.colorBlendOp = VK_BLEND_OP_ADD;
|
||||||
|
att.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
|
||||||
|
att.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
|
||||||
|
att.alphaBlendOp = VK_BLEND_OP_ADD;
|
||||||
cb.attachmentCount = 1;
|
cb.attachmentCount = 1;
|
||||||
cb.pAttachments = &att;
|
cb.pAttachments = &att;
|
||||||
pipelineInfo.pColorBlendState = &cb;
|
pipelineInfo.pColorBlendState = &cb;
|
||||||
@@ -371,6 +709,42 @@ void VulkanGameRenderer::releaseResources()
|
|||||||
|
|
||||||
VkDevice dev = m_window->device();
|
VkDevice dev = m_window->device();
|
||||||
|
|
||||||
|
if (m_sampler)
|
||||||
|
{
|
||||||
|
m_devFuncs->vkDestroySampler(dev, m_sampler, nullptr);
|
||||||
|
m_sampler = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_texStaging)
|
||||||
|
{
|
||||||
|
m_devFuncs->vkDestroyImage(dev, m_texStaging, nullptr);
|
||||||
|
m_texStaging = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_texStagingMem)
|
||||||
|
{
|
||||||
|
m_devFuncs->vkFreeMemory(dev, m_texStagingMem, nullptr);
|
||||||
|
m_texStagingMem = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_texView)
|
||||||
|
{
|
||||||
|
m_devFuncs->vkDestroyImageView(dev, m_texView, nullptr);
|
||||||
|
m_texView = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_texImage)
|
||||||
|
{
|
||||||
|
m_devFuncs->vkDestroyImage(dev, m_texImage, nullptr);
|
||||||
|
m_texImage = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_texMem)
|
||||||
|
{
|
||||||
|
m_devFuncs->vkFreeMemory(dev, m_texMem, nullptr);
|
||||||
|
m_texMem = VK_NULL_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_pipeline)
|
if (m_pipeline)
|
||||||
{
|
{
|
||||||
m_devFuncs->vkDestroyPipeline(dev, m_pipeline, nullptr);
|
m_devFuncs->vkDestroyPipeline(dev, m_pipeline, nullptr);
|
||||||
@@ -420,11 +794,14 @@ void VulkanGameRenderer::startNextFrame()
|
|||||||
VkCommandBuffer cb = m_window->currentCommandBuffer();
|
VkCommandBuffer cb = m_window->currentCommandBuffer();
|
||||||
const QSize sz = m_window->swapChainImageSize();
|
const QSize sz = m_window->swapChainImageSize();
|
||||||
|
|
||||||
|
// Add the necessary barriers and do the host-linear -> device-optimal copy, if not yet done.
|
||||||
|
ensureTexture();
|
||||||
|
|
||||||
VkClearColorValue clearColor = {{ 0, 0, 0, 1 }};
|
VkClearColorValue clearColor = {{ 0, 0, 0, 1 }};
|
||||||
VkClearDepthStencilValue clearDS = { 1, 0 };
|
VkClearDepthStencilValue clearDS = { 1, 0 };
|
||||||
VkClearValue clearValues[3];
|
VkClearValue clearValues[2];
|
||||||
memset(clearValues, 0, sizeof(clearValues));
|
memset(clearValues, 0, sizeof(clearValues));
|
||||||
clearValues[0].color = clearValues[2].color = clearColor;
|
clearValues[0].color = clearColor;
|
||||||
clearValues[1].depthStencil = clearDS;
|
clearValues[1].depthStencil = clearDS;
|
||||||
|
|
||||||
VkRenderPassBeginInfo rpBeginInfo;
|
VkRenderPassBeginInfo rpBeginInfo;
|
||||||
@@ -434,7 +811,7 @@ void VulkanGameRenderer::startNextFrame()
|
|||||||
rpBeginInfo.framebuffer = m_window->currentFramebuffer();
|
rpBeginInfo.framebuffer = m_window->currentFramebuffer();
|
||||||
rpBeginInfo.renderArea.extent.width = sz.width();
|
rpBeginInfo.renderArea.extent.width = sz.width();
|
||||||
rpBeginInfo.renderArea.extent.height = sz.height();
|
rpBeginInfo.renderArea.extent.height = sz.height();
|
||||||
rpBeginInfo.clearValueCount = m_window->sampleCountFlagBits() > VK_SAMPLE_COUNT_1_BIT ? 3 : 2;
|
rpBeginInfo.clearValueCount = 2;
|
||||||
rpBeginInfo.pClearValues = clearValues;
|
rpBeginInfo.pClearValues = clearValues;
|
||||||
VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
|
VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
|
||||||
m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||||
@@ -445,13 +822,10 @@ void VulkanGameRenderer::startNextFrame()
|
|||||||
if (err != VK_SUCCESS)
|
if (err != VK_SUCCESS)
|
||||||
qFatal("Failed to map memory: %d", err);
|
qFatal("Failed to map memory: %d", err);
|
||||||
QMatrix4x4 m = m_proj;
|
QMatrix4x4 m = m_proj;
|
||||||
m.rotate(m_rotation, 0, 1, 0);
|
m.rotate(m_rotation, 0, 0, 1);
|
||||||
memcpy(p, m.constData(), 16 * sizeof(float));
|
memcpy(p, m.constData(), 16 * sizeof(float));
|
||||||
m_devFuncs->vkUnmapMemory(dev, m_bufMem);
|
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->vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipeline);
|
||||||
m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1,
|
m_devFuncs->vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineLayout, 0, 1,
|
||||||
&m_descSet[m_window->currentFrame()], 0, nullptr);
|
&m_descSet[m_window->currentFrame()], 0, nullptr);
|
||||||
@@ -472,7 +846,7 @@ void VulkanGameRenderer::startNextFrame()
|
|||||||
scissor.extent.height = viewport.height;
|
scissor.extent.height = viewport.height;
|
||||||
m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor);
|
m_devFuncs->vkCmdSetScissor(cb, 0, 1, &scissor);
|
||||||
|
|
||||||
m_devFuncs->vkCmdDraw(cb, 3, 1, 0, 0);
|
m_devFuncs->vkCmdDraw(cb, 4, 1, 0, 0);
|
||||||
|
|
||||||
m_devFuncs->vkCmdEndRenderPass(cmdBuf);
|
m_devFuncs->vkCmdEndRenderPass(cmdBuf);
|
||||||
|
|
||||||
|
@@ -1,13 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QVulkanWindow>
|
#include <QVulkanWindow>
|
||||||
|
#include <QImage>
|
||||||
|
|
||||||
struct ProjectContainer;
|
struct ProjectContainer;
|
||||||
|
|
||||||
class VulkanGameRenderer : public QVulkanWindowRenderer
|
class VulkanGameRenderer : public QVulkanWindowRenderer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VulkanGameRenderer(const ProjectContainer &project, QVulkanWindow *w, bool msaa = false);
|
VulkanGameRenderer(const ProjectContainer &project, const float &rotation, QVulkanWindow *w, bool msaa = false);
|
||||||
|
|
||||||
void initResources() override;
|
void initResources() override;
|
||||||
void initSwapChainResources() override;
|
void initSwapChainResources() override;
|
||||||
@@ -18,6 +19,12 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
VkShaderModule createShader(const QString &name);
|
VkShaderModule createShader(const QString &name);
|
||||||
|
bool createTextureFromFile(const QString &name);
|
||||||
|
bool createTexture(const QImage &image);
|
||||||
|
bool createTextureImage(const QSize &size, VkImage *image, VkDeviceMemory *mem,
|
||||||
|
VkImageTiling tiling, VkImageUsageFlags usage, uint32_t memIndex);
|
||||||
|
bool writeLinearImage(const QImage &img, VkImage image, VkDeviceMemory memory);
|
||||||
|
void ensureTexture();
|
||||||
|
|
||||||
QVulkanWindow *m_window;
|
QVulkanWindow *m_window;
|
||||||
QVulkanDeviceFunctions *m_devFuncs;
|
QVulkanDeviceFunctions *m_devFuncs;
|
||||||
@@ -34,8 +41,19 @@ protected:
|
|||||||
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
|
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
|
||||||
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
VkPipeline m_pipeline = VK_NULL_HANDLE;
|
||||||
|
|
||||||
const ProjectContainer &m_project;
|
VkSampler m_sampler = VK_NULL_HANDLE;
|
||||||
|
VkImage m_texImage = VK_NULL_HANDLE;
|
||||||
|
VkDeviceMemory m_texMem = VK_NULL_HANDLE;
|
||||||
|
bool m_texLayoutPending = false;
|
||||||
|
VkImageView m_texView = VK_NULL_HANDLE;
|
||||||
|
VkImage m_texStaging = VK_NULL_HANDLE;
|
||||||
|
VkDeviceMemory m_texStagingMem = VK_NULL_HANDLE;
|
||||||
|
bool m_texStagingPending = false;
|
||||||
|
QSize m_texSize;
|
||||||
|
VkFormat m_texFormat;
|
||||||
|
|
||||||
QMatrix4x4 m_proj;
|
QMatrix4x4 m_proj;
|
||||||
float m_rotation = 0.0f;
|
|
||||||
|
const ProjectContainer &m_project;
|
||||||
|
const float &m_rotation;
|
||||||
};
|
};
|
||||||
|
@@ -2,17 +2,19 @@
|
|||||||
|
|
||||||
#include "vulkangamerenderer.h"
|
#include "vulkangamerenderer.h"
|
||||||
|
|
||||||
VulkanGameWindow::VulkanGameWindow(const ProjectContainer &project, QWindow *parent) :
|
VulkanGameWindow::VulkanGameWindow(const ProjectContainer &project, const float &rotation, QWindow *parent) :
|
||||||
QVulkanWindow{parent},
|
QVulkanWindow{parent},
|
||||||
m_project{project}
|
m_project{project},
|
||||||
|
m_rotation{rotation}
|
||||||
{
|
{
|
||||||
setMinimumWidth(640);
|
setMinimumWidth(640);
|
||||||
setMaximumWidth(640);
|
setMaximumWidth(640);
|
||||||
setMinimumHeight(480);
|
setMinimumHeight(480);
|
||||||
setMaximumHeight(480);
|
setMaximumHeight(480);
|
||||||
|
resize(640, 480);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVulkanWindowRenderer *VulkanGameWindow::createRenderer()
|
QVulkanWindowRenderer *VulkanGameWindow::createRenderer()
|
||||||
{
|
{
|
||||||
return new VulkanGameRenderer{m_project, this, true}; // try MSAA, when available
|
return new VulkanGameRenderer{m_project, m_rotation, this, true}; // try MSAA, when available
|
||||||
}
|
}
|
||||||
|
@@ -8,10 +8,11 @@ struct ProjectContainer;
|
|||||||
class VulkanGameWindow : public QVulkanWindow
|
class VulkanGameWindow : public QVulkanWindow
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit VulkanGameWindow(const ProjectContainer &project, QWindow *parent = nullptr);
|
explicit VulkanGameWindow(const ProjectContainer &project, const float &rotation, QWindow *parent = nullptr);
|
||||||
|
|
||||||
QVulkanWindowRenderer *createRenderer() override;
|
QVulkanWindowRenderer *createRenderer() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const ProjectContainer &m_project;
|
const ProjectContainer &m_project;
|
||||||
|
const float &m_rotation;
|
||||||
};
|
};
|
||||||
|
@@ -1,8 +1,11 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(lcVk, "qt.vulkan")
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
qSetMessagePattern(QStringLiteral("%{time dd.MM.yyyy HH:mm:ss.zzz} "
|
qSetMessagePattern(QStringLiteral("%{time dd.MM.yyyy HH:mm:ss.zzz} "
|
||||||
@@ -16,6 +19,8 @@ int main(int argc, char *argv[])
|
|||||||
"%{function}(): "
|
"%{function}(): "
|
||||||
"%{message}"));
|
"%{message}"));
|
||||||
|
|
||||||
|
QLoggingCategory::setFilterRules(QStringLiteral("qt.vulkan=true"));
|
||||||
|
|
||||||
Q_INIT_RESOURCE(resources_editor);
|
Q_INIT_RESOURCE(resources_editor);
|
||||||
Q_INIT_RESOURCE(resources_engine);
|
Q_INIT_RESOURCE(resources_engine);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user