diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bbaa4c..4ef8f71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/README.md b/README.md index f4a5598..8baf93b 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ # Vulkan Renderer +Created with Qt 5 and the help of +and and . + ## Building -Needs cmake 3.22+, C++20 compiler, Qt 5.10+ +Needs cmake 3.21+, C++20 compiler, Qt 5.10+ ## Usage diff --git a/make_shaders.sh b/make_shaders.sh new file mode 100755 index 0000000..a0d1ac7 --- /dev/null +++ b/make_shaders.sh @@ -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 + diff --git a/shaders/frag.spv b/shaders/frag.spv new file mode 100644 index 0000000..8a4cc40 Binary files /dev/null and b/shaders/frag.spv differ diff --git a/shaders/shader.frag b/shaders/shader.frag new file mode 100644 index 0000000..7c5b0e7 --- /dev/null +++ b/shaders/shader.frag @@ -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); +} diff --git a/shaders/shader.vert b/shaders/shader.vert new file mode 100644 index 0000000..f5b2f8d --- /dev/null +++ b/shaders/shader.vert @@ -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]; +} diff --git a/shaders/vert.spv b/shaders/vert.spv new file mode 100644 index 0000000..ef8a465 Binary files /dev/null and b/shaders/vert.spv differ diff --git a/src/ShaderLoader.cpp b/src/ShaderLoader.cpp new file mode 100644 index 0000000..6be2db1 --- /dev/null +++ b/src/ShaderLoader.cpp @@ -0,0 +1,37 @@ +#include "ShaderLoader.h" +#include + +std::vector 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); + assert(shaderFile.is_open()); + // std::ios::ate starts at the end of file => position is the file size + const std::fpos &fileSize = shaderFile.tellg(); + std::vector buffer(fileSize); + shaderFile.seekg(0); + shaderFile.read(buffer.data(), fileSize); + shaderFile.close(); + + VkShaderModuleCreateInfo createInfo{}; + createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + createInfo.codeSize = buffer.size(); + createInfo.pCode = reinterpret_cast(buffer.data()); + VkShaderModule shaderModule; + VkResult result = deviceFunctions->vkCreateShaderModule( + device, &createInfo, nullptr, &shaderModule); + assert(result == VK_SUCCESS); + loadedShaders.push_back(shaderModule); + } + return loadedShaders; +} + +void ShaderLoader::destroyShaders(VkDevice device, + QVulkanDeviceFunctions *devFuncs) { + for (auto &shaderModule : loadedShaders) { + devFuncs->vkDestroyShaderModule(device, shaderModule, nullptr); + } +} diff --git a/src/ShaderLoader.h b/src/ShaderLoader.h new file mode 100644 index 0000000..03288c6 --- /dev/null +++ b/src/ShaderLoader.h @@ -0,0 +1,34 @@ +#ifndef VULKANCPPSETUP_SHADERLOADER_H +#define VULKANCPPSETUP_SHADERLOADER_H + +#include +#include +#include +#include +#include + +class ShaderLoader { +private: + std::vector loadedShaders; + +public: + ShaderLoader() = default; + ~ShaderLoader() = default; + + std::vector + loadShaders(VkDevice device, QVulkanDeviceFunctions *deviceFunctions, + std::string const *shaderFilenames, + std::size_t shaderFilenameCount); + + template + std::vector + loadShaders(VkDevice device, QVulkanDeviceFunctions *deviceFunctions, + const std::array &shaderFilenames) { + return loadShaders(device, deviceFunctions, shaderFilenames.data(), + shaderFilenames.size()); + } + + void destroyShaders(VkDevice device, QVulkanDeviceFunctions *devFuncs); +}; + +#endif // VULKANCPPSETUP_SHADERLOADER_H diff --git a/src/VulkanRenderer.cpp b/src/VulkanRenderer.cpp index c3a6352..1a0aaab 100644 --- a/src/VulkanRenderer.cpp +++ b/src/VulkanRenderer.cpp @@ -1,21 +1,109 @@ #include "VulkanRenderer.h" +#include VulkanRenderer::VulkanRenderer(QVulkanWindow *w) : m_window(w), m_devFuncs(nullptr) {} void VulkanRenderer::initResources() { - m_devFuncs = - m_window->vulkanInstance()->deviceFunctions(m_window->device()); + m_devFuncs = m_window->vulkanInstance()->deviceFunctions(m_window->device()); + auto shaders = + std::array{"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"; } -void VulkanRenderer::initSwapChainResources() {} -void VulkanRenderer::releaseSwapChainResources() {} -void VulkanRenderer::releaseResources() {} +//void VulkanRenderer::initSwapChainResources() { + // qDebug() << "initSwapChainResources"; + // VkFramebufferCreateInfo framebufferInfo = {}; + // framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + // framebufferInfo.pNext = nullptr; + // framebufferInfo.renderPass = m_renderPass; + // framebufferInfo.attachmentCount = 1; + // framebufferInfo.width = m_window->width(); + // framebufferInfo.height = m_window->height(); + // framebufferInfo.layers = 1; + // + // m_swapChainImages = std::vector(m_window->swapChainImageCount()); + // m_swapChainImageViews = + // std::vector(m_window->swapChainImageCount()); + // m_framebuffers = + // std::vector(m_window->swapChainImageCount()); for (int i = + // 0; i < m_window->swapChainImageCount(); ++i) { + // m_swapChainImages[i] = m_window->swapChainImage(i); + // m_swapChainImageViews[i] = m_window->swapChainImageView(i); + // + // framebufferInfo.pAttachments = &m_swapChainImageViews[i]; + // VkResult vkResult = m_devFuncs->vkCreateFramebuffer( + // m_window->device(), &framebufferInfo, nullptr, &m_framebuffers[i]); + // assert(vkResult == VK_SUCCESS); + // } +//} + +//void VulkanRenderer::releaseSwapChainResources() { + // destruction of swap chain image views are handled in Qt framework + // for (auto& imageView : m_swapChainImageViews) { + // m_devFuncs->vkDestroyImageView(m_window->device(), imageView, nullptr); + // } + // for (auto &framebuffer : m_framebuffers) { + // m_devFuncs->vkDestroyFramebuffer(m_window->device(), framebuffer, + // nullptr); + // } + // destruction of the default render pass is handled in Qt framework + // m_devFuncs->vkDestroyRenderPass(m_window->device(), m_renderPass, + // nullptr); +//} + +void VulkanRenderer::releaseResources() { + m_shaderLoader.destroyShaders(m_window->device(), m_devFuncs); +} void VulkanRenderer::startNextFrame() { VkCommandBuffer cmdBuf = m_window->currentCommandBuffer(); - // m_devFuncs->vkCmdBeginRenderPass(…); + // make a clear-color from frame number. This will flash with a 120*pi frame + // period. + VkClearValue clearValue; + float flash = std::abs(std::sin(static_cast(m_frameNumber) / 120.f)); + clearValue.color = {{0.0f, 0.0f, flash, 1.0f}}; + // start the main renderpass. + // We will use the clear color from above, and the framebuffer of the index + // the swapchain gave us + 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.framebuffer = m_window->currentFramebuffer(); + + // connect clear values + rpInfo.clearValueCount = 1; + rpInfo.pClearValues = &clearValue; + + m_devFuncs->vkCmdBeginRenderPass(cmdBuf, &rpInfo, VK_SUBPASS_CONTENTS_INLINE); + // int imageIndex = m_window->currentSwapChainImageIndex(); + // uint32_t swapchainImageIndex; + // VK_CHECK(vkAcquireNextImageKHR(_device, _swapchain, 1000000000, + // _presentSemaphore, nullptr, &swapchainImageIndex)); + + // m_devFuncs->vkCmdEndRenderPass(cmdBuf); + ++m_frameNumber; m_window->frameReady(); + m_window->requestUpdate(); } diff --git a/src/VulkanRenderer.h b/src/VulkanRenderer.h index 2c68057..4040701 100644 --- a/src/VulkanRenderer.h +++ b/src/VulkanRenderer.h @@ -1,6 +1,7 @@ #ifndef VULKANCPPSETUP_VULKANRENDERER_H #define VULKANCPPSETUP_VULKANRENDERER_H +#include "ShaderLoader.h" #include #include @@ -9,8 +10,6 @@ public: explicit VulkanRenderer(QVulkanWindow *w); void initResources() override; - void initSwapChainResources() override; - void releaseSwapChainResources() override; void releaseResources() override; void startNextFrame() override; @@ -18,6 +17,8 @@ public: private: QVulkanWindow *m_window; QVulkanDeviceFunctions *m_devFuncs; + unsigned long m_frameNumber{0UL}; + ShaderLoader m_shaderLoader; }; #endif // VULKANCPPSETUP_VULKANRENDERER_H diff --git a/src/main.cpp b/src/main.cpp index 466527f..81217be 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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);