Compare commits
2 commits
2ec08561f1
...
e8cd239a6b
Author | SHA1 | Date | |
---|---|---|---|
Ben | e8cd239a6b | ||
Ben | 7127555dea |
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.22)
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
project(VulkanCppSetup)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
@ -6,6 +6,6 @@ set(CMAKE_CXX_STANDARD 20)
|
|||
find_package(Vulkan REQUIRED)
|
||||
find_package(Qt5 COMPONENTS Gui REQUIRED)
|
||||
|
||||
add_executable(VulkanCppSetup src/main.cpp src/VulkanWindow.cpp src/VulkanRenderer.cpp)
|
||||
add_executable(VulkanCppSetup src/main.cpp src/VulkanWindow.cpp src/VulkanRenderer.cpp src/ShaderLoader.cpp)
|
||||
#include_directories(VulkanCppSetup ${Vulkan_INCLUDE_DIRS} ${Qt5_INCLUDE_DIRS})
|
||||
target_link_libraries(VulkanCppSetup ${Vulkan_LIBRARIES} Qt5::Gui)
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
# Vulkan Renderer
|
||||
|
||||
Created with Qt 5 and the help of <https://vulkan-tutorial.com>
|
||||
and <https://vkguide.dev/> and <https://doc.qt.io/qt-5/>.
|
||||
|
||||
## Building
|
||||
|
||||
Needs cmake 3.22+, C++20 compiler, Qt 5.10+
|
||||
Needs cmake 3.21+, C++20 compiler, Qt 5.10+
|
||||
|
||||
## Usage
|
||||
|
||||
|
|
9
make_shaders.sh
Executable file
9
make_shaders.sh
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh
|
||||
|
||||
DIR="$(realpath $(dirname $0))"
|
||||
cd "${DIR}/shaders"
|
||||
|
||||
# needs Vulkan SDK
|
||||
glslc shader.vert -o vert.spv
|
||||
glslc shader.frag -o frag.spv
|
||||
|
BIN
shaders/frag.spv
Normal file
BIN
shaders/frag.spv
Normal file
Binary file not shown.
9
shaders/shader.frag
Normal file
9
shaders/shader.frag
Normal file
|
@ -0,0 +1,9 @@
|
|||
#version 450
|
||||
|
||||
layout(location = 0) in vec3 fragColor;
|
||||
|
||||
layout(location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outColor = vec4(fragColor, 1.0);
|
||||
}
|
20
shaders/shader.vert
Normal file
20
shaders/shader.vert
Normal file
|
@ -0,0 +1,20 @@
|
|||
#version 450
|
||||
|
||||
layout(location = 0) out vec3 fragColor;
|
||||
|
||||
vec2 positions[3] = vec2[](
|
||||
vec2(0.0, -0.5),
|
||||
vec2(0.5, 0.5),
|
||||
vec2(-0.5, 0.5)
|
||||
);
|
||||
|
||||
vec3 colors[3] = vec3[](
|
||||
vec3(1.0, 0.0, 0.0),
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(0.0, 0.0, 1.0)
|
||||
);
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
|
||||
fragColor = colors[gl_VertexIndex];
|
||||
}
|
BIN
shaders/vert.spv
Normal file
BIN
shaders/vert.spv
Normal file
Binary file not shown.
41
src/ShaderLoader.cpp
Normal file
41
src/ShaderLoader.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#include "ShaderLoader.h"
|
||||
#include <fstream>
|
||||
|
||||
std::vector<VkShaderModule> ShaderLoader::loadShaders(
|
||||
VkDevice device, QVulkanDeviceFunctions *deviceFunctions,
|
||||
std::string const *shaderFilenames, std::size_t shaderFilenameCount) {
|
||||
loadedShaders.reserve(loadedShaders.size() + shaderFilenameCount);
|
||||
for (int i = 0; i < shaderFilenameCount; ++i) {
|
||||
std::ifstream shaderFile(shaderFilenames[i],
|
||||
std::ios::ate | std::ios::binary);
|
||||
if (!shaderFile.is_open()) {
|
||||
qFatal("Could not open shader file %s", shaderFilenames[i].c_str());
|
||||
}
|
||||
// std::ios::ate starts at the end of file => position is the file size
|
||||
const std::fpos<mbstate_t> &fileSize = shaderFile.tellg();
|
||||
std::vector<uint32_t> buffer((fileSize / sizeof(uint32_t)) + 1);
|
||||
shaderFile.seekg(0);
|
||||
shaderFile.read(reinterpret_cast<char *>(buffer.data()), fileSize);
|
||||
shaderFile.close();
|
||||
|
||||
VkShaderModuleCreateInfo createInfo{};
|
||||
createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
createInfo.codeSize = fileSize;
|
||||
createInfo.pCode = buffer.data();
|
||||
VkShaderModule shaderModule;
|
||||
VkResult result = deviceFunctions->vkCreateShaderModule(
|
||||
device, &createInfo, nullptr, &shaderModule);
|
||||
if (result != VK_SUCCESS) {
|
||||
qFatal("Could not create shader module: code %d", result);
|
||||
}
|
||||
loadedShaders.push_back(shaderModule);
|
||||
}
|
||||
return loadedShaders;
|
||||
}
|
||||
|
||||
void ShaderLoader::destroyShaders(VkDevice device,
|
||||
QVulkanDeviceFunctions *devFuncs) {
|
||||
for (auto &shaderModule : loadedShaders) {
|
||||
devFuncs->vkDestroyShaderModule(device, shaderModule, nullptr);
|
||||
}
|
||||
}
|
34
src/ShaderLoader.h
Normal file
34
src/ShaderLoader.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef VULKANCPPSETUP_SHADERLOADER_H
|
||||
#define VULKANCPPSETUP_SHADERLOADER_H
|
||||
|
||||
#include <array>
|
||||
#include <qvulkanfunctions.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
class ShaderLoader {
|
||||
private:
|
||||
std::vector<VkShaderModule> loadedShaders{};
|
||||
|
||||
public:
|
||||
ShaderLoader() = default;
|
||||
~ShaderLoader() = default;
|
||||
|
||||
std::vector<VkShaderModule>
|
||||
loadShaders(VkDevice device, QVulkanDeviceFunctions *deviceFunctions,
|
||||
std::string const *shaderFilenames,
|
||||
std::size_t shaderFilenameCount);
|
||||
|
||||
template <std::size_t amount>
|
||||
std::vector<VkShaderModule>
|
||||
loadShaders(VkDevice device, QVulkanDeviceFunctions *deviceFunctions,
|
||||
const std::array<std::string, amount> &shaderFilenames) {
|
||||
return loadShaders(device, deviceFunctions, shaderFilenames.data(),
|
||||
shaderFilenames.size());
|
||||
}
|
||||
|
||||
void destroyShaders(VkDevice device, QVulkanDeviceFunctions *devFuncs);
|
||||
};
|
||||
|
||||
#endif // VULKANCPPSETUP_SHADERLOADER_H
|
|
@ -1,21 +1,178 @@
|
|||
#include "VulkanRenderer.h"
|
||||
#include <complex>
|
||||
|
||||
VulkanRenderer::VulkanRenderer(QVulkanWindow *w)
|
||||
: m_window(w), m_devFuncs(nullptr) {}
|
||||
VulkanRenderer::VulkanRenderer(QVulkanWindow *w) : m_window(w) {}
|
||||
|
||||
void VulkanRenderer::initResources() {
|
||||
m_devFuncs =
|
||||
m_window->vulkanInstance()->deviceFunctions(m_window->device());
|
||||
VkResult result;
|
||||
m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device());
|
||||
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
|
||||
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipelineLayoutInfo.setLayoutCount = 0; // Optional
|
||||
pipelineLayoutInfo.pSetLayouts = nullptr; // Optional
|
||||
pipelineLayoutInfo.pushConstantRangeCount = 0; // Optional
|
||||
pipelineLayoutInfo.pPushConstantRanges = nullptr; // Optional
|
||||
|
||||
result = m_devFuncs->vkCreatePipelineLayout(
|
||||
m_window->device(), &pipelineLayoutInfo, nullptr, &m_pipelineLayout);
|
||||
if (result != VK_SUCCESS) {
|
||||
qFatal("Failed to create pipeline layout, code: %d", result);
|
||||
}
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
|
||||
vertexInputInfo.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
vertexInputInfo.vertexBindingDescriptionCount = 0;
|
||||
vertexInputInfo.pVertexBindingDescriptions = nullptr; // Optional
|
||||
vertexInputInfo.vertexAttributeDescriptionCount = 0;
|
||||
vertexInputInfo.pVertexAttributeDescriptions = nullptr; // Optional
|
||||
|
||||
auto shaders =
|
||||
std::array<std::string, 2>{"shaders/vert.spv", "shaders/frag.spv"};
|
||||
auto loadedShaders =
|
||||
m_shaderLoader.loadShaders(m_window->device(), m_devFuncs, shaders);
|
||||
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
|
||||
vertShaderStageInfo.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||
vertShaderStageInfo.module = loadedShaders[0];
|
||||
vertShaderStageInfo.pName = "main";
|
||||
VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
|
||||
fragShaderStageInfo.sType =
|
||||
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
fragShaderStageInfo.module = loadedShaders[1];
|
||||
fragShaderStageInfo.pName = "main";
|
||||
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages{
|
||||
vertShaderStageInfo, fragShaderStageInfo};
|
||||
VkGraphicsPipelineCreateInfo pipelineInfo{};
|
||||
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipelineInfo.stageCount = shaderStages.size();
|
||||
pipelineInfo.pStages = shaderStages.data();
|
||||
pipelineInfo.pVertexInputState = &vertexInputInfo;
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo ia;
|
||||
memset(&ia, 0, sizeof(ia));
|
||||
ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
pipelineInfo.pInputAssemblyState = &ia;
|
||||
|
||||
VkPipelineViewportStateCreateInfo vp;
|
||||
memset(&vp, 0, sizeof(vp));
|
||||
vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
vp.viewportCount = 1;
|
||||
vp.scissorCount = 1;
|
||||
pipelineInfo.pViewportState = &vp;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rs;
|
||||
memset(&rs, 0, sizeof(rs));
|
||||
rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
rs.polygonMode = VK_POLYGON_MODE_FILL;
|
||||
// rs.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||
rs.cullMode = VK_CULL_MODE_NONE;
|
||||
rs.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
rs.lineWidth = 1.0f;
|
||||
pipelineInfo.pRasterizationState = &rs;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo ms;
|
||||
memset(&ms, 0, sizeof(ms));
|
||||
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
ms.rasterizationSamples = m_window->sampleCountFlagBits();
|
||||
pipelineInfo.pMultisampleState = &ms;
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo ds;
|
||||
memset(&ds, 0, sizeof(ds));
|
||||
ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
ds.depthTestEnable = VK_FALSE;
|
||||
ds.depthWriteEnable = VK_TRUE;
|
||||
ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
|
||||
pipelineInfo.pDepthStencilState = &ds;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo cb;
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
VkPipelineColorBlendAttachmentState att;
|
||||
memset(&att, 0, sizeof(att));
|
||||
att.colorWriteMask = 0xF;
|
||||
cb.attachmentCount = 1;
|
||||
cb.pAttachments = &att;
|
||||
pipelineInfo.pColorBlendState = &cb;
|
||||
|
||||
VkDynamicState dynamicStates[] = {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(dynamicStates) / sizeof(VkDynamicState);
|
||||
dyn.pDynamicStates = dynamicStates;
|
||||
pipelineInfo.pDynamicState = &dyn;
|
||||
|
||||
pipelineInfo.layout = m_pipelineLayout;
|
||||
pipelineInfo.renderPass = m_window->defaultRenderPass();
|
||||
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
|
||||
pipelineInfo.basePipelineIndex = -1; // Optional
|
||||
|
||||
result = m_devFuncs->vkCreateGraphicsPipelines(
|
||||
m_window->device(), VK_NULL_HANDLE, 1, &pipelineInfo, nullptr,
|
||||
&m_graphicsPipeline);
|
||||
if (result != VK_SUCCESS) {
|
||||
qFatal("Failed to create graphics pipeline: code %d", result);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanRenderer::initSwapChainResources() {}
|
||||
void VulkanRenderer::releaseSwapChainResources() {}
|
||||
void VulkanRenderer::releaseResources() {}
|
||||
void VulkanRenderer::releaseResources() {
|
||||
m_devFuncs->vkDestroyPipeline(m_window->device(), m_graphicsPipeline,
|
||||
nullptr);
|
||||
m_devFuncs->vkDestroyPipelineLayout(m_window->device(), m_pipelineLayout,
|
||||
nullptr);
|
||||
m_shaderLoader.destroyShaders(m_window->device(), m_devFuncs);
|
||||
}
|
||||
|
||||
void VulkanRenderer::startNextFrame() {
|
||||
VkCommandBuffer cmdBuf = m_window->currentCommandBuffer();
|
||||
|
||||
// m_devFuncs->vkCmdBeginRenderPass(…);
|
||||
VkClearDepthStencilValue clearDS = {1, 0};
|
||||
std::array<VkClearValue, 3> clearValue{};
|
||||
float flash = std::abs(std::sin(static_cast<float>(m_frameNumber) / 120.f));
|
||||
clearValue[0].color = {{0.0f, 0.0f, flash, 1.0f}};
|
||||
clearValue[1].depthStencil = clearDS;
|
||||
clearValue[2].color = clearValue[0].color;
|
||||
|
||||
VkRenderPassBeginInfo rpInfo{};
|
||||
rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
rpInfo.pNext = nullptr;
|
||||
|
||||
rpInfo.renderPass = m_window->defaultRenderPass();
|
||||
rpInfo.renderArea.offset.x = 0;
|
||||
rpInfo.renderArea.offset.y = 0;
|
||||
rpInfo.renderArea.extent.width = m_window->width();
|
||||
rpInfo.renderArea.extent.height = m_window->height();
|
||||
rpInfo.clearValueCount =
|
||||
m_window->sampleCountFlagBits() > VK_SAMPLE_COUNT_1_BIT ? 3 : 2;
|
||||
rpInfo.pClearValues = clearValue.data();
|
||||
rpInfo.framebuffer = m_window->currentFramebuffer();
|
||||
|
||||
m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
const QSize renderTargetSize = m_window->swapChainImageSize();
|
||||
VkViewport viewport = {
|
||||
0, 0, float(renderTargetSize.width()), float(renderTargetSize.height()),
|
||||
0, 1};
|
||||
m_devFuncs->vkCmdSetViewport(cmdBuf, 0, 1, &viewport);
|
||||
|
||||
VkRect2D scissor = {{0, 0},
|
||||
{uint32_t(renderTargetSize.width()),
|
||||
uint32_t(renderTargetSize.height())}};
|
||||
m_devFuncs->vkCmdSetScissor(cmdBuf, 0, 1, &scissor);
|
||||
|
||||
m_devFuncs->vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
||||
m_graphicsPipeline);
|
||||
|
||||
m_devFuncs->vkCmdDraw(cmdBuf, 3, 1, 0, 0);
|
||||
|
||||
m_devFuncs->vkCmdEndRenderPass(cmdBuf);
|
||||
m_window->frameReady();
|
||||
++m_frameNumber;
|
||||
m_window->requestUpdate();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef VULKANCPPSETUP_VULKANRENDERER_H
|
||||
#define VULKANCPPSETUP_VULKANRENDERER_H
|
||||
|
||||
#include "ShaderLoader.h"
|
||||
#include <QVulkanWindowRenderer>
|
||||
#include <qvulkanfunctions.h>
|
||||
|
||||
|
@ -9,15 +10,17 @@ public:
|
|||
explicit VulkanRenderer(QVulkanWindow *w);
|
||||
|
||||
void initResources() override;
|
||||
void initSwapChainResources() override;
|
||||
void releaseSwapChainResources() override;
|
||||
void releaseResources() override;
|
||||
|
||||
void startNextFrame() override;
|
||||
|
||||
private:
|
||||
QVulkanWindow *m_window;
|
||||
QVulkanDeviceFunctions *m_devFuncs;
|
||||
QVulkanDeviceFunctions *m_devFuncs{};
|
||||
unsigned long m_frameNumber{0UL};
|
||||
ShaderLoader m_shaderLoader{};
|
||||
VkPipelineLayout m_pipelineLayout{};
|
||||
VkPipeline m_graphicsPipeline{};
|
||||
};
|
||||
|
||||
#endif // VULKANCPPSETUP_VULKANRENDERER_H
|
||||
|
|
|
@ -6,9 +6,8 @@ static const int KEY_ESCAPE = 16777216;
|
|||
|
||||
void VulkanWindow::keyPressEvent(QKeyEvent *event) {
|
||||
QWindow::keyPressEvent(event);
|
||||
qDebug() << "text" << event->text()
|
||||
<< "key" << event->key()
|
||||
<< "modifiers" << event->modifiers();
|
||||
qDebug() << "text" << event->text() << "key" << event->key() << "modifiers"
|
||||
<< event->modifiers();
|
||||
if (event->matches(QKeySequence::Quit) || event->key() == KEY_ESCAPE) {
|
||||
close();
|
||||
}
|
||||
|
|
|
@ -5,18 +5,16 @@
|
|||
#ifndef VULKANCPPSETUP_VULKANWINDOW_H
|
||||
#define VULKANCPPSETUP_VULKANWINDOW_H
|
||||
|
||||
#include <QVulkanWindow>
|
||||
#include <iostream>
|
||||
#include <qevent.h>
|
||||
#include <QVulkanWindow>
|
||||
|
||||
class VulkanWindow : public QVulkanWindow
|
||||
{
|
||||
class VulkanWindow : public QVulkanWindow {
|
||||
public:
|
||||
QVulkanWindowRenderer *createRenderer() override;
|
||||
QVulkanWindowRenderer *createRenderer() override;
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent* event) override;
|
||||
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
};
|
||||
|
||||
#endif //VULKANCPPSETUP_VULKANWINDOW_H
|
||||
#endif // VULKANCPPSETUP_VULKANWINDOW_H
|
||||
|
|
|
@ -8,9 +8,10 @@ int main(int argc, char **argv) {
|
|||
QVulkanInstance inst;
|
||||
QByteArrayList layers{"VK_LAYER_LUNARG_standard_validation"};
|
||||
inst.setLayers(layers);
|
||||
if (!inst.create())
|
||||
if (!inst.create()) {
|
||||
qFatal("Could not create Vulkan instance %d", inst.errorCode());
|
||||
return EXIT_FAILURE;
|
||||
|
||||
}
|
||||
VulkanWindow window{};
|
||||
window.setVulkanInstance(&inst);
|
||||
window.resize(800, 600);
|
||||
|
|
Loading…
Reference in a new issue