This commit is contained in:
Ilya Shurupov 2024-11-26 12:55:16 +03:00
parent f5bd6f85a8
commit dc88120f58
29 changed files with 321 additions and 278 deletions

3
.gitmodules vendored
View file

@ -1,3 +1,6 @@
[submodule "extern/Modules"] [submodule "extern/Modules"]
path = extern/Modules path = extern/Modules
url = https://github.com/elushaX/Modules.git url = https://github.com/elushaX/Modules.git
[submodule "extern/imgui"]
path = extern/imgui
url = https://github.com/ocornut/imgui.git

View file

@ -4,8 +4,7 @@ project(App)
set(CMAKE_CXX_STANDARD 26) set(CMAKE_CXX_STANDARD 26)
add_subdirectory(extern/Modules) add_subdirectory(extern/Modules)
add_subdirectory(extern/Utils)
add_subdirectory(src/Common) add_subdirectory(src/VulkanGraphics)
add_subdirectory(src/GPU)
add_subdirectory(src/PlatformWindow)
add_subdirectory(src/App) add_subdirectory(src/App)

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.30) cmake_minimum_required(VERSION 3.30)
project(Common) project(Utils)
file(GLOB SOURCES "./private/*.cpp") file(GLOB SOURCES "./private/*.cpp")
add_library(${PROJECT_NAME} ${SOURCES}) add_library(${PROJECT_NAME} ${SOURCES})

1
extern/imgui vendored Submodule

@ -0,0 +1 @@
Subproject commit 5b7feebfd8f3e58392d955b4c8ae01f77eae25ed

View file

@ -5,6 +5,6 @@ project(App)
file(GLOB SOURCES "./private/*.cpp") file(GLOB SOURCES "./private/*.cpp")
add_executable(${PROJECT_NAME} ${SOURCES}) add_executable(${PROJECT_NAME} ${SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC public) target_include_directories(${PROJECT_NAME} PUBLIC public)
target_link_libraries(${PROJECT_NAME} PUBLIC Common GPU PlatformWindow) target_link_libraries(${PROJECT_NAME} PUBLIC VulkanGraphics)
file(COPY shaders DESTINATION ${PROJECT_BINARY_DIR}/) file(COPY shaders DESTINATION ${PROJECT_BINARY_DIR}/)

View file

@ -1,12 +1,29 @@
#include "app_interactive.hpp" #include "VulkanWindow.hpp"
#include "app_offscreen.hpp" #include "VulkanOffscreen.hpp"
#include "renderer.hpp"
#include <iostream> #include <iostream>
int runOffscreen() { int runOffscreen() {
try { try {
AppOffscreen app; VulkanOffscreen app;
app.run(); Renderer renderer;
renderer.create(app.getRenderInfo());
app.setRenderPass(renderer.getRenderPass());
auto frameData = app.beginDrawFrame();
renderer.fillRenderCommands(frameData.commandBuffer, frameData.framebuffer, frameData.extent2D);
renderer.updateUniformBuffer({frameData.extent2D.width, frameData.extent2D.height});
app.endDrawFrame(frameData);
app.saveFrame();
renderer.destroy(app.getDevice());
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
@ -16,10 +33,28 @@ int runOffscreen() {
} }
int runInteractive() { int runInteractive() {
AppInteractive app;
try { try {
app.run(); Renderer renderer;
VulkanWindow app;
renderer.create(app.getRenderInfo());
app.setRenderPass(renderer.getRenderPass());
while (!glfwWindowShouldClose(app.getWindow())) {
app.pollEvents();
auto drawData = app.startDrawFrame();
renderer.fillRenderCommands(drawData.commandBuffer, drawData.frameBuffer, drawData.extent2D);
renderer.updateUniformBuffer({drawData.extent2D.width, drawData.extent2D.height});
app.endDrawFrame(drawData);
}
app.waitDevice();
renderer.destroy(app.getDevice());
} catch (const std::exception &e) { } catch (const std::exception &e) {
std::cerr << e.what() << std::endl; std::cerr << e.what() << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;

View file

@ -1,10 +1,10 @@
#include "renderer.hpp" #include "renderer.hpp"
#include "vulkan_utils.hpp" #include "VulkanUtils.hpp"
#include <stdexcept> #include <stdexcept>
#include <cstring> #include <cstring>
void Renderer::create(const CreateInfo &info) { void Renderer::create(const RenderCreateInfo &info) {
mShader.create(info.device); mShader.create(info.device);
createRenderPass(info.device, info.format, info.finalImageLayout); createRenderPass(info.device, info.format, info.finalImageLayout);
@ -371,7 +371,7 @@ void Renderer::destroyBuffer(VkDevice device, VkBuffer buffer, VkDeviceMemory me
} }
void void
Renderer::populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend) { Renderer::fillRenderCommands(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend) {
VkCommandBufferBeginInfo commandBufferBeginInfo{ VkCommandBufferBeginInfo commandBufferBeginInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
}; };
@ -471,4 +471,25 @@ void Renderer::copyBuffer(VkDevice device, VkCommandPool commandPool, VkQueue qu
vkQueueWaitIdle(queue); vkQueueWaitIdle(queue);
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
}
void Renderer::updateUniformBuffer(const std::pair<uint32_t, uint32_t>& frameBufferSize) const {
static float time = 0;
time += 0.001;
glm::mat4 model = glm::rotate(glm::mat4(1.0f), (time * glm::radians(90.f)), glm::vec3(0.f, 0.f, 1.f));
glm::mat4 view = glm::lookAt(glm::vec3{2.0f, 2.0f, 2.0f}, glm::vec3{0.0f, 0.0f, 0.0f}, -glm::vec3{0.0f, 0.0f, 1.0f});
glm::mat4 perspective = glm::perspective(glm::radians(45.f),
(float) frameBufferSize.first /
(float) frameBufferSize.second, 0.1f, 10.f);
auto transforms = perspective * view * model;
CustomShader::UniformBuffer ubo{
.transforms = transforms,
.origin = glm::vec4(0, 0, 0, 0),
};
memcpy(mUniformBufferMemoryMapped, &ubo, sizeof(ubo));
// ubo.transforms =
} }

View file

@ -3,39 +3,11 @@
#include "shader.hpp" #include "shader.hpp"
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
struct Renderer { class Renderer {
CustomShader mShader; public:
VkPipeline mGraphicsPipeline = VK_NULL_HANDLE; Renderer() = default;
VkRenderPass mGraphicsRenderPass = VK_NULL_HANDLE;
VkPipelineLayout mGraphicsPipelineLayout{}; // no uniforms used in the shader
// Buffers void create(const RenderCreateInfo& info);
VkBuffer mVertexBuffer = VK_NULL_HANDLE;
VkDeviceMemory mVertexBufferMemory = VK_NULL_HANDLE;
VkBuffer mIndexBuffer = VK_NULL_HANDLE;
VkDeviceMemory mIndexBufferMemory = VK_NULL_HANDLE;
VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE;
VkDescriptorSet mDescriptorSet = VK_NULL_HANDLE;
VkBuffer mUniformBuffer = VK_NULL_HANDLE;
VkDeviceMemory mUniformBufferMemory = VK_NULL_HANDLE;
void* mUniformBufferMemoryMapped = nullptr;
struct CreateInfo {
VkDevice device;
VkPhysicalDevice physicalDevice;
VkExtent2D extent2D;
VkFormat format;
VkCommandPool commandPool;
VkQueue queue;
VkImageLayout finalImageLayout;
};
void create(const CreateInfo& info);
void destroy(VkDevice device) const; void destroy(VkDevice device) const;
@ -56,7 +28,7 @@ struct Renderer {
void createUniformBuffer(VkPhysicalDevice physicalDevice, VkDevice device); void createUniformBuffer(VkPhysicalDevice physicalDevice, VkDevice device);
void populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend); void fillRenderCommands(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend);
struct BufferCreateInfo { struct BufferCreateInfo {
VkBuffer *buffer; VkBuffer *buffer;
@ -74,4 +46,28 @@ struct Renderer {
static void destroyBuffer(VkDevice device, VkBuffer buffer, VkDeviceMemory memory); static void destroyBuffer(VkDevice device, VkBuffer buffer, VkDeviceMemory memory);
void destroyGraphicsPipeline(VkDevice device) const; void destroyGraphicsPipeline(VkDevice device) const;
void updateUniformBuffer(const std::pair<uint32_t, uint32_t>& frameBufferSize) const;
VkRenderPass getRenderPass() { return mGraphicsRenderPass; }
private:
CustomShader mShader;
VkPipeline mGraphicsPipeline = VK_NULL_HANDLE;
VkRenderPass mGraphicsRenderPass = VK_NULL_HANDLE;
VkPipelineLayout mGraphicsPipelineLayout{}; // no uniforms used in the shader
// Buffers
VkBuffer mVertexBuffer = VK_NULL_HANDLE;
VkDeviceMemory mVertexBufferMemory = VK_NULL_HANDLE;
VkBuffer mIndexBuffer = VK_NULL_HANDLE;
VkDeviceMemory mIndexBufferMemory = VK_NULL_HANDLE;
VkDescriptorPool mDescriptorPool = VK_NULL_HANDLE;
VkDescriptorSet mDescriptorSet = VK_NULL_HANDLE;
VkBuffer mUniformBuffer = VK_NULL_HANDLE;
VkDeviceMemory mUniformBufferMemory = VK_NULL_HANDLE;
void* mUniformBufferMemoryMapped = nullptr;
}; };

View file

@ -1,8 +1,8 @@
#pragma once #pragma once
#include "common.hpp" #include "VulkanInterface.hpp"
#include <vulkan/vulkan.h> #include "common.hpp"
struct CustomShader { struct CustomShader {

View file

@ -1 +0,0 @@
#pragma once

View file

@ -1,8 +0,0 @@
cmake_minimum_required(VERSION 3.30)
project(PlatformWindow)
file(GLOB SOURCES "./private/*.cpp")
add_library(${PROJECT_NAME} ${SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC public)
target_link_libraries(${PROJECT_NAME} PUBLIC glfw GPU)

View file

@ -1 +0,0 @@
#pragma once

View file

@ -1,8 +1,8 @@
cmake_minimum_required(VERSION 3.30) cmake_minimum_required(VERSION 3.30)
project(GPU) project(VulkanGraphics)
file(GLOB SOURCES "./private/*.cpp") file(GLOB SOURCES "./private/*.cpp")
add_library(${PROJECT_NAME} ${SOURCES}) add_library(${PROJECT_NAME} ${SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC public) target_include_directories(${PROJECT_NAME} PUBLIC public)
target_link_libraries(${PROJECT_NAME} PUBLIC vulkan Common) target_link_libraries(${PROJECT_NAME} PUBLIC vulkan glfw Containers Math Utils)

View file

@ -1,4 +1,5 @@
#include "swapchain.hpp" #include "SwapChain.hpp"
#include <stdexcept> #include <stdexcept>
#include <numeric> #include <numeric>
#include <algorithm> #include <algorithm>

View file

@ -1,23 +1,15 @@
#include "app_offscreen.hpp" #include "VulkanOffscreen.hpp"
#include "Buffer2D.hpp"
#include "Color.hpp"
#include "utils.hpp" #include "utils.hpp"
AppOffscreen::AppOffscreen() { VulkanOffscreen::VulkanOffscreen() {
initDevice(); initDevice();
initPipeline(); initPipeline();
initFramebuffer();
initStagingBuffer();
} }
void AppOffscreen::run() { VulkanOffscreen::~VulkanOffscreen() {
updateUniformBuffer();
draw();
transitionImageLayout();
transferToHost();
readMemory();
}
AppOffscreen::~AppOffscreen() {
vkDeviceWaitIdle(mDevice); vkDeviceWaitIdle(mDevice);
finalizeStagingBuffer(); finalizeStagingBuffer();
@ -26,7 +18,55 @@ AppOffscreen::~AppOffscreen() {
finalizeDevice(); finalizeDevice();
} }
void AppOffscreen::initDevice() { RenderCreateInfo VulkanOffscreen::getRenderInfo() {
return {
.device = mDevice,
.physicalDevice = mPhysicalDevice,
.extent2D = mExtent,
.format = mFormat,
.commandPool = mCommandPool,
.queue = mGraphicsQueue,
.finalImageLayout = VK_IMAGE_LAYOUT_GENERAL,
// .finalImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
}
void VulkanOffscreen::setRenderPass(VkRenderPass renderPass) {
initFramebuffer(renderPass);
initStagingBuffer();
}
VulkanOffscreen::FrameData VulkanOffscreen::beginDrawFrame() {
return {
mCommandBuffer, mFramebuffer, mExtent,
};
}
void VulkanOffscreen::endDrawFrame(const FrameData&) {
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
VkSubmitInfo submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.pWaitDstStageMask = waitStages,
.commandBufferCount = 1,
.pCommandBuffers = &mCommandBuffer,
.signalSemaphoreCount = 0,
.pSignalSemaphores = nullptr,
};
if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, nullptr) != VK_SUCCESS) {
throw std::runtime_error("failed to submit to graphics queue");
}
}
void VulkanOffscreen::saveFrame() {
transferToHost();
readMemory();
}
void VulkanOffscreen::initDevice() {
std::vector<const char*> extensions = {}; std::vector<const char*> extensions = {};
std::vector<const char*> validationLayers = {}; std::vector<const char*> validationLayers = {};
std::vector<const char*> deviceExtensions = {}; std::vector<const char*> deviceExtensions = {};
@ -97,8 +137,7 @@ void AppOffscreen::initDevice() {
vkGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue); vkGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
} }
void AppOffscreen::initPipeline() { void VulkanOffscreen::initPipeline() {
mShader.create(mDevice);
VkCommandPoolCreateInfo commandPoolCreateInfo { VkCommandPoolCreateInfo commandPoolCreateInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
@ -117,22 +156,9 @@ void AppOffscreen::initPipeline() {
}; };
vkAllocateCommandBuffers(mDevice, &allocateInfo, &mCommandBuffer); vkAllocateCommandBuffers(mDevice, &allocateInfo, &mCommandBuffer);
Renderer::CreateInfo renderCreateInfo {
.device = mDevice,
.physicalDevice = mPhysicalDevice,
.extent2D = mExtent,
.format = mFormat,
.commandPool = mCommandPool,
.queue = mGraphicsQueue,
.finalImageLayout = VK_IMAGE_LAYOUT_GENERAL,
// .finalImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
};
mRenderer.create(renderCreateInfo);
} }
void AppOffscreen::initFramebuffer() { void VulkanOffscreen::initFramebuffer(VkRenderPass renderPass) {
// vkGetDeviceImageMemoryRequirements(); // vkGetDeviceImageMemoryRequirements();
VkImageCreateInfo imageCreateInfo { VkImageCreateInfo imageCreateInfo {
@ -193,7 +219,7 @@ void AppOffscreen::initFramebuffer() {
VkFramebufferCreateInfo createInfo { VkFramebufferCreateInfo createInfo {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = mRenderer.mGraphicsRenderPass, .renderPass = renderPass,
.attachmentCount = 1, .attachmentCount = 1,
.pAttachments = &mImageView, .pAttachments = &mImageView,
.width = mExtent.width, .width = mExtent.width,
@ -206,32 +232,7 @@ void AppOffscreen::initFramebuffer() {
} }
} }
void AppOffscreen::draw() { void VulkanOffscreen::transferToHost() {
mRenderer.populateGraphicsCommandBuffer(mCommandBuffer, mFramebuffer, mExtent);
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
VkSubmitInfo submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.pWaitDstStageMask = waitStages,
.commandBufferCount = 1,
.pCommandBuffers = &mCommandBuffer,
.signalSemaphoreCount = 0,
.pSignalSemaphores = nullptr,
};
if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, nullptr) != VK_SUCCESS) {
throw std::runtime_error("failed to submit to graphics queue");
}
}
void AppOffscreen::transitionImageLayout() {
}
void AppOffscreen::transferToHost() {
vkDeviceWaitIdle(mDevice); vkDeviceWaitIdle(mDevice);
VkBufferImageCopy stagingBufferToImageCopy { VkBufferImageCopy stagingBufferToImageCopy {
@ -289,7 +290,7 @@ void AppOffscreen::transferToHost() {
} }
} }
void AppOffscreen::readMemory() { void VulkanOffscreen::readMemory() {
void* data; void* data;
uint32_t size = mExtent.height * mExtent.width * sizeof(float) * 4; uint32_t size = mExtent.height * mExtent.width * sizeof(float) * 4;
if (vkMapMemory(mDevice, mStagingBufferMemory, 0, size, 0, &data) != VK_SUCCESS) { if (vkMapMemory(mDevice, mStagingBufferMemory, 0, size, 0, &data) != VK_SUCCESS) {
@ -304,25 +305,7 @@ void AppOffscreen::readMemory() {
vkUnmapMemory(mDevice, mStagingBufferMemory); vkUnmapMemory(mDevice, mStagingBufferMemory);
} }
void AppOffscreen::updateUniformBuffer() const { void VulkanOffscreen::initStagingBuffer() {
static float time = 0;
time += 0.001;
glm::mat4 model = glm::rotate(glm::mat4(1.0f), (time * glm::radians(90.f)), glm::vec3(0.f, 0.f, 1.f));
glm::mat4 view = glm::lookAt(glm::vec3{2.0f, 2.0f, 2.0f}, glm::vec3{0.0f, 0.0f, 0.0f}, -glm::vec3{0.0f, 0.0f, 1.0f});
glm::mat4 perspective = glm::perspective(glm::radians(45.f), (float) mExtent.width / (float) mExtent.height, 0.1f, 10.f);
auto transforms = perspective * view * model;
CustomShader::UniformBuffer ubo{
.transforms = transforms,
.origin = glm::vec4(0, 0, 0, 0),
};
memcpy(mRenderer.mUniformBufferMemoryMapped, &ubo, sizeof(ubo));
}
void AppOffscreen::initStagingBuffer() {
VkBufferCreateInfo stagingBufferCreateInfo { VkBufferCreateInfo stagingBufferCreateInfo {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
@ -346,27 +329,24 @@ void AppOffscreen::initStagingBuffer() {
vkBindBufferMemory(mDevice, mStagingBuffer, mStagingBufferMemory, 0); vkBindBufferMemory(mDevice, mStagingBuffer, mStagingBufferMemory, 0);
} }
void AppOffscreen::finalizeStagingBuffer() { void VulkanOffscreen::finalizeStagingBuffer() {
vkFreeMemory(mDevice, mStagingBufferMemory, nullptr); vkFreeMemory(mDevice, mStagingBufferMemory, nullptr);
vkDestroyBuffer(mDevice, mStagingBuffer, nullptr); vkDestroyBuffer(mDevice, mStagingBuffer, nullptr);
} }
void AppOffscreen::finalizeFramebuffer() { void VulkanOffscreen::finalizeFramebuffer() {
vkFreeMemory(mDevice, mFramebufferMemory, nullptr); vkFreeMemory(mDevice, mFramebufferMemory, nullptr);
vkDestroyFramebuffer(mDevice, mFramebuffer, nullptr); vkDestroyFramebuffer(mDevice, mFramebuffer, nullptr);
vkDestroyImageView(mDevice, mImageView, nullptr); vkDestroyImageView(mDevice, mImageView, nullptr);
vkDestroyImage(mDevice, mImage, nullptr); vkDestroyImage(mDevice, mImage, nullptr);
} }
void AppOffscreen::finalizePipeline() { void VulkanOffscreen::finalizePipeline() {
vkFreeCommandBuffers(mDevice, mCommandPool, 1, &mCommandBuffer); vkFreeCommandBuffers(mDevice, mCommandPool, 1, &mCommandBuffer);
vkDestroyCommandPool(mDevice, mCommandPool, nullptr); vkDestroyCommandPool(mDevice, mCommandPool, nullptr);
mShader.destroy(mDevice);
mRenderer.destroy(mDevice);
} }
void AppOffscreen::finalizeDevice() { void VulkanOffscreen::finalizeDevice() {
vkDestroyDevice(mDevice, nullptr); vkDestroyDevice(mDevice, nullptr);
vkDestroyInstance(mInstance, nullptr); vkDestroyInstance(mInstance, nullptr);
} }

View file

@ -1,4 +1,4 @@
#include "vulkan_utils.hpp" #include "VulkanUtils.hpp"
#include <cstring> #include <cstring>
#include <stdexcept> #include <stdexcept>

View file

@ -1,32 +1,69 @@
#include "app_interactive.hpp" #include "VulkanWindow.hpp"
#include "SwapChain.hpp"
#include <vulkan/vulkan.h>
#include "renderer.hpp"
#include "swapchain.hpp"
#include "vulkan_utils.hpp"
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <stdexcept> #include <stdexcept>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <chrono>
VulkanWindow::VulkanWindow() {
mSwapChain = new SwapChain();
void AppInteractive::run() {
initWindow(); initWindow();
initVulkan(); initVulkan();
mainLoop();
cleanup();
} }
void AppInteractive::scheduleWindowResize(int sizeX, int sizeY) { VulkanWindow::~VulkanWindow() {
cleanup();
delete mSwapChain;
}
RenderCreateInfo VulkanWindow::getRenderInfo() {
return {
mDevice, mPhysicalDevice,
mSwapChain->mSwapChainExtent, mSwapChain->mSwapChainFormat,
mCommandPool, mGraphicsQueue, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
};
}
GLFWwindow* VulkanWindow::getWindow() {
return mWindow;
}
VkDevice VulkanWindow::getDevice() {
return mDevice;
}
void VulkanWindow::setRenderPass(VkRenderPass renderPass) {
mRenderPass = renderPass;
mSwapChain->createSwapChainFramebuffers(mDevice, mRenderPass);
}
void VulkanWindow::pollEvents() {
glfwPollEvents();
if (mWindowSizeDirtyFlag) {
if (timeDeltaMs(mWindowSizeDirtyFlagTime) > mWindowSizeApplyMinDelay) {
recreateSwapChain(mWindowFramebufferSize.first, mWindowFramebufferSize.second);
mWindowSizeDirtyFlag = false;
}
}
}
void VulkanWindow::waitDevice() {
vkDeviceWaitIdle(mDevice);
}
void VulkanWindow::scheduleWindowResize(int sizeX, int sizeY) {
mWindowSizeDirtyFlagTime = std::chrono::system_clock::now(); mWindowSizeDirtyFlagTime = std::chrono::system_clock::now();
mWindowSizeDirtyFlag = true; mWindowSizeDirtyFlag = true;
mWindowFramebufferSize = std::make_pair(sizeX, sizeY); mWindowFramebufferSize = std::make_pair(sizeX, sizeY);
assert(!(sizeX <= 0 || sizeY <= 0)); assert(!(sizeX <= 0 || sizeY <= 0));
} }
void AppInteractive::initWindow() { void VulkanWindow::initWindow() {
// glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11); // glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11);
glfwInit(); glfwInit();
@ -37,11 +74,11 @@ void AppInteractive::initWindow() {
glfwSetWindowUserPointer(mWindow, this); glfwSetWindowUserPointer(mWindow, this);
glfwSetFramebufferSizeCallback(mWindow, [](GLFWwindow *window, int sizeX, int sizeY) { glfwSetFramebufferSizeCallback(mWindow, [](GLFWwindow *window, int sizeX, int sizeY) {
((AppInteractive *) glfwGetWindowUserPointer(window))->scheduleWindowResize(sizeX, sizeY); ((VulkanWindow *) glfwGetWindowUserPointer(window))->scheduleWindowResize(sizeX, sizeY);
}); });
} }
void AppInteractive::initVulkan() { void VulkanWindow::initVulkan() {
const std::vector<const char *> deviceExtensions = { const std::vector<const char *> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_SWAPCHAIN_EXTENSION_NAME,
}; };
@ -105,44 +142,16 @@ void AppInteractive::initVulkan() {
.presentationQueueFamilyIndex = (uint32_t) mPresentationQueueFamilyIndex, .presentationQueueFamilyIndex = (uint32_t) mPresentationQueueFamilyIndex,
}; };
mSwapChain.createSwapChain(swapChainCreateInfo); mSwapChain->createSwapChain(swapChainCreateInfo);
mSwapChain.createSwapChainImageViews(mDevice); mSwapChain->createSwapChainImageViews(mDevice);
createCommandPool(); createCommandPool();
createCommandBuffer(); createCommandBuffer();
Renderer::CreateInfo rendererCreateInfo{
mDevice, mPhysicalDevice,
mSwapChain.mSwapChainExtent, mSwapChain.mSwapChainFormat,
mCommandPool, mGraphicsQueue, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
};
mRenderer.create(rendererCreateInfo);
mSwapChain.createSwapChainFramebuffers(mDevice, mRenderer.mGraphicsRenderPass);
createSynchronizationObjects(); createSynchronizationObjects();
} }
void AppInteractive::mainLoop() { void VulkanWindow::createLogicalDevice(const std::vector<const char *> &deviceExtensions) {
while (!glfwWindowShouldClose(mWindow)) {
glfwPollEvents();
if (mWindowSizeDirtyFlag) {
auto timeDelayMs = timeDeltaMs(mWindowSizeDirtyFlagTime);
if (timeDelayMs > mWindowSizeApplyMinDelay) {
recreateSwapChain(mWindowFramebufferSize.first, mWindowFramebufferSize.second);
mWindowSizeDirtyFlag = false;
}
}
drawFrame();
}
vkDeviceWaitIdle(mDevice);
}
void AppInteractive::createLogicalDevice(const std::vector<const char *> &deviceExtensions) {
float queuePriority = 1.f; float queuePriority = 1.f;
VkDeviceQueueCreateInfo graphicsQueueCreateInfos{ VkDeviceQueueCreateInfo graphicsQueueCreateInfos{
@ -181,21 +190,21 @@ void AppInteractive::createLogicalDevice(const std::vector<const char *> &device
if (mDevice == VK_NULL_HANDLE) throw std::runtime_error("failed to create vulkan logical device"); if (mDevice == VK_NULL_HANDLE) throw std::runtime_error("failed to create vulkan logical device");
} }
void AppInteractive::getQueues() { void VulkanWindow::getQueues() {
vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, 0, &mGraphicsQueue); vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, 0, &mGraphicsQueue);
vkGetDeviceQueue(mDevice, mPresentationQueueFamilyIndex, 0, &mPresentQueue); vkGetDeviceQueue(mDevice, mPresentationQueueFamilyIndex, 0, &mPresentQueue);
} }
void AppInteractive::createWindowSurface() { void VulkanWindow::createWindowSurface() {
if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) { if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) {
throw std::runtime_error("failed to create vulkan window surface"); throw std::runtime_error("failed to create vulkan window surface");
} }
} }
void AppInteractive::recreateSwapChain(int sizeX, int sizeY) { void VulkanWindow::recreateSwapChain(int sizeX, int sizeY) {
vkDeviceWaitIdle(mDevice); vkDeviceWaitIdle(mDevice);
mSwapChain.destroySwapChain(mDevice); mSwapChain->destroySwapChain(mDevice);
SwapChain::CreateInfo createInfo{ SwapChain::CreateInfo createInfo{
.device = mDevice, .device = mDevice,
@ -207,13 +216,12 @@ void AppInteractive::recreateSwapChain(int sizeX, int sizeY) {
.presentationQueueFamilyIndex = (uint32_t) mPresentationQueueFamilyIndex, .presentationQueueFamilyIndex = (uint32_t) mPresentationQueueFamilyIndex,
}; };
mSwapChain.createSwapChain(createInfo); mSwapChain->createSwapChain(createInfo);
mSwapChain.createSwapChainImageViews(mDevice); mSwapChain->createSwapChainImageViews(mDevice);
mSwapChain.createSwapChainFramebuffers(mDevice, mRenderer.mGraphicsRenderPass); mSwapChain->createSwapChainFramebuffers(mDevice, mRenderPass);
} }
void VulkanWindow::createSynchronizationObjects() {
void AppInteractive::createSynchronizationObjects() {
VkSemaphoreCreateInfo semaphoreCreateInfo{ VkSemaphoreCreateInfo semaphoreCreateInfo{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
}; };
@ -232,18 +240,27 @@ void AppInteractive::createSynchronizationObjects() {
if (failure) throw std::runtime_error("failed to create synchronization objects"); if (failure) throw std::runtime_error("failed to create synchronization objects");
} }
void AppInteractive::drawFrame() { VulkanWindow::DrawFrame VulkanWindow::startDrawFrame() {
vkWaitForFences(mDevice, 1, &mFenceCanStartNewFrame, VK_TRUE, UINT64_MAX); vkWaitForFences(mDevice, 1, &mFenceCanStartNewFrame, VK_TRUE, UINT64_MAX);
vkResetFences(mDevice, 1, &mFenceCanStartNewFrame); vkResetFences(mDevice, 1, &mFenceCanStartNewFrame);
uint32_t imageIndex = 0; uint32_t imageIndex = 0;
vkAcquireNextImageKHR(mDevice, mSwapChain.mSwapChain, UINT64_MAX, mSemaphoreImageAcquired, VK_NULL_HANDLE, vkAcquireNextImageKHR(mDevice, mSwapChain->mSwapChain, UINT64_MAX, mSemaphoreImageAcquired, VK_NULL_HANDLE,
&imageIndex); &imageIndex);
vkResetCommandBuffer(mCommandBuffer, 0); vkResetCommandBuffer(mCommandBuffer, 0);
mRenderer.populateGraphicsCommandBuffer(mCommandBuffer, mSwapChain.mSwapChainFrameBuffers[imageIndex],
mSwapChain.mSwapChainExtent);
VkFramebuffer framebuffer = mSwapChain->mSwapChainFrameBuffers[imageIndex];
return {
.commandBuffer = mCommandBuffer,
.frameBuffer = framebuffer,
.extent2D = mSwapChain->mSwapChainExtent,
.swapChainImageIndex = imageIndex,
};
}
void VulkanWindow::endDrawFrame(const DrawFrame& frame) {
VkSemaphore waitSemaphores[] = {mSemaphoreImageAcquired}; VkSemaphore waitSemaphores[] = {mSemaphoreImageAcquired};
VkSemaphore signalSemaphores[] = {mSemaphoreFramebufferDrawn}; VkSemaphore signalSemaphores[] = {mSemaphoreFramebufferDrawn};
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
@ -259,13 +276,11 @@ void AppInteractive::drawFrame() {
.pSignalSemaphores = signalSemaphores, .pSignalSemaphores = signalSemaphores,
}; };
updateUniformBuffer();
if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mFenceCanStartNewFrame) != VK_SUCCESS) { if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mFenceCanStartNewFrame) != VK_SUCCESS) {
throw std::runtime_error("failed to submit to graphics queue"); throw std::runtime_error("failed to submit to graphics queue");
} }
VkSwapchainKHR swapchains[] = {mSwapChain.mSwapChain}; VkSwapchainKHR swapchains[] = {mSwapChain->mSwapChain};
VkPresentInfoKHR presentInfo{ VkPresentInfoKHR presentInfo{
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
@ -273,53 +288,23 @@ void AppInteractive::drawFrame() {
.pWaitSemaphores = signalSemaphores, .pWaitSemaphores = signalSemaphores,
.swapchainCount = 1, .swapchainCount = 1,
.pSwapchains = swapchains, .pSwapchains = swapchains,
.pImageIndices = &imageIndex, .pImageIndices = &frame.swapChainImageIndex,
}; };
vkQueuePresentKHR(mPresentQueue, &presentInfo); vkQueuePresentKHR(mPresentQueue, &presentInfo);
/* Somehow res is always VK_SUCCESS
if (res == VK_ERROR_OUT_OF_DATE_KHR || res == VK_SUBOPTIMAL_KHR) {
recreateSwapChain();
} else if (res != VK_SUCCESS) {
throw std::runtime_error("cannot acquire new khr image");
}
*/
} }
void AppInteractive::updateUniformBuffer() const { void VulkanWindow::destroySynchronizationObjects() {
static float time = 0;
time += 0.001;
glm::mat4 model = glm::rotate(glm::mat4(1.0f), (time * glm::radians(90.f)), glm::vec3(0.f, 0.f, 1.f));
glm::mat4 view = glm::lookAt(glm::vec3{2.0f, 2.0f, 2.0f}, glm::vec3{0.0f, 0.0f, 0.0f}, -glm::vec3{0.0f, 0.0f, 1.0f});
glm::mat4 perspective = glm::perspective(glm::radians(45.f),
(float) mWindowFramebufferSize.first /
(float) mWindowFramebufferSize.second, 0.1f, 10.f);
auto transforms = perspective * view * model;
CustomShader::UniformBuffer ubo{
.transforms = transforms,
.origin = glm::vec4(0, 0, 0, 0),
};
memcpy(mRenderer.mUniformBufferMemoryMapped, &ubo, sizeof(ubo));
// ubo.transforms =
}
void AppInteractive::destroySynchronizationObjects() {
vkDestroySemaphore(mDevice, mSemaphoreImageAcquired, nullptr); vkDestroySemaphore(mDevice, mSemaphoreImageAcquired, nullptr);
vkDestroySemaphore(mDevice, mSemaphoreFramebufferDrawn, nullptr); vkDestroySemaphore(mDevice, mSemaphoreFramebufferDrawn, nullptr);
vkDestroyFence(mDevice, mFenceCanStartNewFrame, nullptr); vkDestroyFence(mDevice, mFenceCanStartNewFrame, nullptr);
} }
void AppInteractive::cleanup() { void VulkanWindow::cleanup() {
mRenderer.destroy(mDevice);
destroyCommandPool(); destroyCommandPool();
destroySynchronizationObjects(); destroySynchronizationObjects();
mSwapChain.destroySwapChain(mDevice); mSwapChain->destroySwapChain(mDevice);
vkDestroyDevice(mDevice, nullptr); vkDestroyDevice(mDevice, nullptr);
vkDestroySurfaceKHR(mInstance, mSurface, nullptr); vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
@ -328,7 +313,7 @@ void AppInteractive::cleanup() {
glfwTerminate(); glfwTerminate();
} }
void AppInteractive::createCommandPool() { void VulkanWindow::createCommandPool() {
VkCommandPoolCreateInfo createInfo{ VkCommandPoolCreateInfo createInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
@ -340,7 +325,7 @@ void AppInteractive::createCommandPool() {
} }
} }
void AppInteractive::createCommandBuffer() { void VulkanWindow::createCommandBuffer() {
VkCommandBufferAllocateInfo allocateInfo{ VkCommandBufferAllocateInfo allocateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = mCommandPool, .commandPool = mCommandPool,
@ -353,6 +338,6 @@ void AppInteractive::createCommandBuffer() {
} }
} }
void AppInteractive::destroyCommandPool() { void VulkanWindow::destroyCommandPool() {
vkDestroyCommandPool(mDevice, mCommandPool, nullptr); vkDestroyCommandPool(mDevice, mCommandPool, nullptr);
} }

View file

@ -0,0 +1,15 @@
#pragma once
#include <vulkan/vulkan.h>
struct RenderCreateInfo {
VkDevice device;
VkPhysicalDevice physicalDevice;
VkExtent2D extent2D;
VkFormat format;
VkCommandPool commandPool;
VkQueue queue;
VkImageLayout finalImageLayout;
};

View file

@ -1,39 +1,44 @@
#pragma once #pragma once
#include "vulkan_utils.hpp" #include "VulkanUtils.hpp"
#include <stdexcept> #include <stdexcept>
#include <functional> #include <functional>
#include <cstring> #include <cstring>
#include "renderer.hpp" class VulkanOffscreen {
#include "shader.hpp"
class AppOffscreen {
public: public:
AppOffscreen(); VulkanOffscreen();
~VulkanOffscreen();
void run(); RenderCreateInfo getRenderInfo();
void setRenderPass(VkRenderPass renderPass);
~AppOffscreen(); struct FrameData {
VkCommandBuffer commandBuffer;
VkFramebuffer framebuffer;
VkExtent2D extent2D;
};
FrameData beginDrawFrame();
void endDrawFrame(const FrameData& frame);
void saveFrame();
VkDevice getDevice() { return mDevice; }
private: private:
void initDevice(); void initDevice();
void initPipeline(); void initPipeline();
void initFramebuffer(); void initFramebuffer(VkRenderPass renderPass);
void initStagingBuffer(); void initStagingBuffer();
void draw();
void transitionImageLayout();
void readMemory(); void readMemory();
void updateUniformBuffer() const;
void transferToHost(); void transferToHost();
void finalizeStagingBuffer(); void finalizeStagingBuffer();
@ -70,7 +75,4 @@ private:
VkBuffer mStagingBuffer = VK_NULL_HANDLE; VkBuffer mStagingBuffer = VK_NULL_HANDLE;
VkDeviceMemory mStagingBufferMemory = VK_NULL_HANDLE; VkDeviceMemory mStagingBufferMemory = VK_NULL_HANDLE;
Renderer mRenderer;
CustomShader mShader;
}; };

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <vulkan/vulkan.h> #include "VulkanInterface.hpp"
#include <vector> #include <vector>
#include <functional> #include <functional>
@ -15,4 +16,4 @@ int findQueueFamilyIndex(VkPhysicalDevice physicalDevice, const QueuePickPolicy&
uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags flags); uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, VkMemoryPropertyFlags flags);
VkInstance createInstance(const char ** instanceExtensions, uint32_t extensionsCount); VkInstance createInstance(const char ** instanceExtensions, uint32_t extensionsCount);
VkPhysicalDevice pickPhysicalDevice(VkInstance instance, const PhysicalDevicePickPolicy& pickPolicy); VkPhysicalDevice pickPhysicalDevice(VkInstance instance, const PhysicalDevicePickPolicy& pickPolicy);

View file

@ -1,16 +1,35 @@
#pragma once #pragma once
#include "vulkan_utils.hpp" #include "VulkanUtils.hpp"
#include "renderer.hpp"
#include "swapchain.hpp"
#include "utils.hpp" #include "utils.hpp"
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
class AppInteractive { class SwapChain;
class VulkanWindow {
public: public:
void run(); VulkanWindow();
~VulkanWindow();
GLFWwindow* getWindow();
VkDevice getDevice();
RenderCreateInfo getRenderInfo();
void setRenderPass(VkRenderPass renderPass);
struct DrawFrame{
VkCommandBuffer commandBuffer;
VkFramebuffer frameBuffer;
VkExtent2D extent2D;
uint32_t swapChainImageIndex;
};
void pollEvents();
void waitDevice();
DrawFrame startDrawFrame();
void endDrawFrame(const DrawFrame& frame);
void scheduleWindowResize(int sizeX, int sizeY); void scheduleWindowResize(int sizeX, int sizeY);
@ -19,8 +38,6 @@ private:
void initVulkan(); void initVulkan();
void mainLoop();
void createLogicalDevice(const std::vector<const char *> &deviceExtensions); void createLogicalDevice(const std::vector<const char *> &deviceExtensions);
void getQueues(); void getQueues();
@ -31,10 +48,6 @@ private:
void createSynchronizationObjects(); void createSynchronizationObjects();
void drawFrame();
void updateUniformBuffer() const;
void destroySynchronizationObjects(); void destroySynchronizationObjects();
void cleanup(); void cleanup();
@ -67,9 +80,10 @@ private:
VkQueue mPresentQueue = VK_NULL_HANDLE; VkQueue mPresentQueue = VK_NULL_HANDLE;
VkSurfaceKHR mSurface = VK_NULL_HANDLE; VkSurfaceKHR mSurface = VK_NULL_HANDLE;
SwapChain mSwapChain; SwapChain* mSwapChain = nullptr;
Renderer mRenderer; VkRenderPass mRenderPass = VK_NULL_HANDLE;
// Renderer* mRenderer = nullptr;
VkSemaphore mSemaphoreImageAcquired = VK_NULL_HANDLE; VkSemaphore mSemaphoreImageAcquired = VK_NULL_HANDLE;
VkSemaphore mSemaphoreFramebufferDrawn = VK_NULL_HANDLE; VkSemaphore mSemaphoreFramebufferDrawn = VK_NULL_HANDLE;