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"]
path = extern/Modules
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)
add_subdirectory(extern/Modules)
add_subdirectory(extern/Utils)
add_subdirectory(src/Common)
add_subdirectory(src/GPU)
add_subdirectory(src/PlatformWindow)
add_subdirectory(src/VulkanGraphics)
add_subdirectory(src/App)

View file

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.30)
project(Common)
project(Utils)
file(GLOB SOURCES "./private/*.cpp")
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")
add_executable(${PROJECT_NAME} ${SOURCES})
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}/)

View file

@ -1,12 +1,29 @@
#include "app_interactive.hpp"
#include "app_offscreen.hpp"
#include "VulkanWindow.hpp"
#include "VulkanOffscreen.hpp"
#include "renderer.hpp"
#include <iostream>
int runOffscreen() {
try {
AppOffscreen app;
app.run();
VulkanOffscreen app;
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) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
@ -16,10 +33,28 @@ int runOffscreen() {
}
int runInteractive() {
AppInteractive app;
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) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;

View file

@ -1,10 +1,10 @@
#include "renderer.hpp"
#include "vulkan_utils.hpp"
#include "VulkanUtils.hpp"
#include <stdexcept>
#include <cstring>
void Renderer::create(const CreateInfo &info) {
void Renderer::create(const RenderCreateInfo &info) {
mShader.create(info.device);
createRenderPass(info.device, info.format, info.finalImageLayout);
@ -371,7 +371,7 @@ void Renderer::destroyBuffer(VkDevice device, VkBuffer buffer, VkDeviceMemory me
}
void
Renderer::populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend) {
Renderer::fillRenderCommands(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend) {
VkCommandBufferBeginInfo commandBufferBeginInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
};
@ -472,3 +472,24 @@ void Renderer::copyBuffer(VkDevice device, VkCommandPool commandPool, VkQueue qu
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 <vulkan/vulkan.h>
struct Renderer {
CustomShader mShader;
VkPipeline mGraphicsPipeline = VK_NULL_HANDLE;
VkRenderPass mGraphicsRenderPass = VK_NULL_HANDLE;
VkPipelineLayout mGraphicsPipelineLayout{}; // no uniforms used in the shader
class Renderer {
public:
Renderer() = default;
// 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;
struct CreateInfo {
VkDevice device;
VkPhysicalDevice physicalDevice;
VkExtent2D extent2D;
VkFormat format;
VkCommandPool commandPool;
VkQueue queue;
VkImageLayout finalImageLayout;
};
void create(const CreateInfo& info);
void create(const RenderCreateInfo& info);
void destroy(VkDevice device) const;
@ -56,7 +28,7 @@ struct Renderer {
void createUniformBuffer(VkPhysicalDevice physicalDevice, VkDevice device);
void populateGraphicsCommandBuffer(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend);
void fillRenderCommands(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, VkExtent2D extend);
struct BufferCreateInfo {
VkBuffer *buffer;
@ -74,4 +46,28 @@ struct Renderer {
static void destroyBuffer(VkDevice device, VkBuffer buffer, VkDeviceMemory memory);
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
#include "common.hpp"
#include "VulkanInterface.hpp"
#include <vulkan/vulkan.h>
#include "common.hpp"
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)
project(GPU)
project(VulkanGraphics)
file(GLOB SOURCES "./private/*.cpp")
add_library(${PROJECT_NAME} ${SOURCES})
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 <numeric>
#include <algorithm>

View file

@ -1,23 +1,15 @@
#include "app_offscreen.hpp"
#include "VulkanOffscreen.hpp"
#include "Buffer2D.hpp"
#include "Color.hpp"
#include "utils.hpp"
AppOffscreen::AppOffscreen() {
VulkanOffscreen::VulkanOffscreen() {
initDevice();
initPipeline();
initFramebuffer();
initStagingBuffer();
}
void AppOffscreen::run() {
updateUniformBuffer();
draw();
transitionImageLayout();
transferToHost();
readMemory();
}
AppOffscreen::~AppOffscreen() {
VulkanOffscreen::~VulkanOffscreen() {
vkDeviceWaitIdle(mDevice);
finalizeStagingBuffer();
@ -26,7 +18,55 @@ AppOffscreen::~AppOffscreen() {
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*> validationLayers = {};
std::vector<const char*> deviceExtensions = {};
@ -97,8 +137,7 @@ void AppOffscreen::initDevice() {
vkGetDeviceQueue(mDevice, mGraphicsQueueIndex, 0, &mGraphicsQueue);
}
void AppOffscreen::initPipeline() {
mShader.create(mDevice);
void VulkanOffscreen::initPipeline() {
VkCommandPoolCreateInfo commandPoolCreateInfo {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
@ -117,22 +156,9 @@ void AppOffscreen::initPipeline() {
};
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();
VkImageCreateInfo imageCreateInfo {
@ -193,7 +219,7 @@ void AppOffscreen::initFramebuffer() {
VkFramebufferCreateInfo createInfo {
.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
.renderPass = mRenderer.mGraphicsRenderPass,
.renderPass = renderPass,
.attachmentCount = 1,
.pAttachments = &mImageView,
.width = mExtent.width,
@ -206,32 +232,7 @@ void AppOffscreen::initFramebuffer() {
}
}
void AppOffscreen::draw() {
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() {
void VulkanOffscreen::transferToHost() {
vkDeviceWaitIdle(mDevice);
VkBufferImageCopy stagingBufferToImageCopy {
@ -289,7 +290,7 @@ void AppOffscreen::transferToHost() {
}
}
void AppOffscreen::readMemory() {
void VulkanOffscreen::readMemory() {
void* data;
uint32_t size = mExtent.height * mExtent.width * sizeof(float) * 4;
if (vkMapMemory(mDevice, mStagingBufferMemory, 0, size, 0, &data) != VK_SUCCESS) {
@ -304,25 +305,7 @@ void AppOffscreen::readMemory() {
vkUnmapMemory(mDevice, mStagingBufferMemory);
}
void AppOffscreen::updateUniformBuffer() 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) 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() {
void VulkanOffscreen::initStagingBuffer() {
VkBufferCreateInfo stagingBufferCreateInfo {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
@ -346,27 +329,24 @@ void AppOffscreen::initStagingBuffer() {
vkBindBufferMemory(mDevice, mStagingBuffer, mStagingBufferMemory, 0);
}
void AppOffscreen::finalizeStagingBuffer() {
void VulkanOffscreen::finalizeStagingBuffer() {
vkFreeMemory(mDevice, mStagingBufferMemory, nullptr);
vkDestroyBuffer(mDevice, mStagingBuffer, nullptr);
}
void AppOffscreen::finalizeFramebuffer() {
void VulkanOffscreen::finalizeFramebuffer() {
vkFreeMemory(mDevice, mFramebufferMemory, nullptr);
vkDestroyFramebuffer(mDevice, mFramebuffer, nullptr);
vkDestroyImageView(mDevice, mImageView, nullptr);
vkDestroyImage(mDevice, mImage, nullptr);
}
void AppOffscreen::finalizePipeline() {
void VulkanOffscreen::finalizePipeline() {
vkFreeCommandBuffers(mDevice, mCommandPool, 1, &mCommandBuffer);
vkDestroyCommandPool(mDevice, mCommandPool, nullptr);
mShader.destroy(mDevice);
mRenderer.destroy(mDevice);
}
void AppOffscreen::finalizeDevice() {
void VulkanOffscreen::finalizeDevice() {
vkDestroyDevice(mDevice, nullptr);
vkDestroyInstance(mInstance, nullptr);
}

View file

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

View file

@ -1,32 +1,69 @@
#include "app_interactive.hpp"
#include <vulkan/vulkan.h>
#include "renderer.hpp"
#include "swapchain.hpp"
#include "vulkan_utils.hpp"
#include "VulkanWindow.hpp"
#include "SwapChain.hpp"
#include <GLFW/glfw3.h>
#include <stdexcept>
#include <cassert>
#include <cstring>
#include <chrono>
VulkanWindow::VulkanWindow() {
mSwapChain = new SwapChain();
void AppInteractive::run() {
initWindow();
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();
mWindowSizeDirtyFlag = true;
mWindowFramebufferSize = std::make_pair(sizeX, sizeY);
assert(!(sizeX <= 0 || sizeY <= 0));
}
void AppInteractive::initWindow() {
void VulkanWindow::initWindow() {
// glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_X11);
glfwInit();
@ -37,11 +74,11 @@ void AppInteractive::initWindow() {
glfwSetWindowUserPointer(mWindow, this);
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 = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
};
@ -105,44 +142,16 @@ void AppInteractive::initVulkan() {
.presentationQueueFamilyIndex = (uint32_t) mPresentationQueueFamilyIndex,
};
mSwapChain.createSwapChain(swapChainCreateInfo);
mSwapChain.createSwapChainImageViews(mDevice);
mSwapChain->createSwapChain(swapChainCreateInfo);
mSwapChain->createSwapChainImageViews(mDevice);
createCommandPool();
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();
}
void AppInteractive::mainLoop() {
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) {
void VulkanWindow::createLogicalDevice(const std::vector<const char *> &deviceExtensions) {
float queuePriority = 1.f;
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");
}
void AppInteractive::getQueues() {
void VulkanWindow::getQueues() {
vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, 0, &mGraphicsQueue);
vkGetDeviceQueue(mDevice, mPresentationQueueFamilyIndex, 0, &mPresentQueue);
}
void AppInteractive::createWindowSurface() {
void VulkanWindow::createWindowSurface() {
if (glfwCreateWindowSurface(mInstance, mWindow, nullptr, &mSurface) != VK_SUCCESS) {
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);
mSwapChain.destroySwapChain(mDevice);
mSwapChain->destroySwapChain(mDevice);
SwapChain::CreateInfo createInfo{
.device = mDevice,
@ -207,13 +216,12 @@ void AppInteractive::recreateSwapChain(int sizeX, int sizeY) {
.presentationQueueFamilyIndex = (uint32_t) mPresentationQueueFamilyIndex,
};
mSwapChain.createSwapChain(createInfo);
mSwapChain.createSwapChainImageViews(mDevice);
mSwapChain.createSwapChainFramebuffers(mDevice, mRenderer.mGraphicsRenderPass);
mSwapChain->createSwapChain(createInfo);
mSwapChain->createSwapChainImageViews(mDevice);
mSwapChain->createSwapChainFramebuffers(mDevice, mRenderPass);
}
void AppInteractive::createSynchronizationObjects() {
void VulkanWindow::createSynchronizationObjects() {
VkSemaphoreCreateInfo semaphoreCreateInfo{
.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");
}
void AppInteractive::drawFrame() {
VulkanWindow::DrawFrame VulkanWindow::startDrawFrame() {
vkWaitForFences(mDevice, 1, &mFenceCanStartNewFrame, VK_TRUE, UINT64_MAX);
vkResetFences(mDevice, 1, &mFenceCanStartNewFrame);
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);
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 signalSemaphores[] = {mSemaphoreFramebufferDrawn};
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
@ -259,13 +276,11 @@ void AppInteractive::drawFrame() {
.pSignalSemaphores = signalSemaphores,
};
updateUniformBuffer();
if (vkQueueSubmit(mGraphicsQueue, 1, &submitInfo, mFenceCanStartNewFrame) != VK_SUCCESS) {
throw std::runtime_error("failed to submit to graphics queue");
}
VkSwapchainKHR swapchains[] = {mSwapChain.mSwapChain};
VkSwapchainKHR swapchains[] = {mSwapChain->mSwapChain};
VkPresentInfoKHR presentInfo{
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
@ -273,53 +288,23 @@ void AppInteractive::drawFrame() {
.pWaitSemaphores = signalSemaphores,
.swapchainCount = 1,
.pSwapchains = swapchains,
.pImageIndices = &imageIndex,
.pImageIndices = &frame.swapChainImageIndex,
};
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 {
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() {
void VulkanWindow::destroySynchronizationObjects() {
vkDestroySemaphore(mDevice, mSemaphoreImageAcquired, nullptr);
vkDestroySemaphore(mDevice, mSemaphoreFramebufferDrawn, nullptr);
vkDestroyFence(mDevice, mFenceCanStartNewFrame, nullptr);
}
void AppInteractive::cleanup() {
mRenderer.destroy(mDevice);
void VulkanWindow::cleanup() {
destroyCommandPool();
destroySynchronizationObjects();
mSwapChain.destroySwapChain(mDevice);
mSwapChain->destroySwapChain(mDevice);
vkDestroyDevice(mDevice, nullptr);
vkDestroySurfaceKHR(mInstance, mSurface, nullptr);
@ -328,7 +313,7 @@ void AppInteractive::cleanup() {
glfwTerminate();
}
void AppInteractive::createCommandPool() {
void VulkanWindow::createCommandPool() {
VkCommandPoolCreateInfo createInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
@ -340,7 +325,7 @@ void AppInteractive::createCommandPool() {
}
}
void AppInteractive::createCommandBuffer() {
void VulkanWindow::createCommandBuffer() {
VkCommandBufferAllocateInfo allocateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = mCommandPool,
@ -353,6 +338,6 @@ void AppInteractive::createCommandBuffer() {
}
}
void AppInteractive::destroyCommandPool() {
void VulkanWindow::destroyCommandPool() {
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
#include "vulkan_utils.hpp"
#include "VulkanUtils.hpp"
#include <stdexcept>
#include <functional>
#include <cstring>
#include "renderer.hpp"
#include "shader.hpp"
class AppOffscreen {
class VulkanOffscreen {
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:
void initDevice();
void initPipeline();
void initFramebuffer();
void initFramebuffer(VkRenderPass renderPass);
void initStagingBuffer();
void draw();
void transitionImageLayout();
void readMemory();
void updateUniformBuffer() const;
void transferToHost();
void finalizeStagingBuffer();
@ -70,7 +75,4 @@ private:
VkBuffer mStagingBuffer = VK_NULL_HANDLE;
VkDeviceMemory mStagingBufferMemory = VK_NULL_HANDLE;
Renderer mRenderer;
CustomShader mShader;
};

View file

@ -1,6 +1,7 @@
#pragma once
#include <vulkan/vulkan.h>
#include "VulkanInterface.hpp"
#include <vector>
#include <functional>

View file

@ -1,16 +1,35 @@
#pragma once
#include "vulkan_utils.hpp"
#include "VulkanUtils.hpp"
#include "renderer.hpp"
#include "swapchain.hpp"
#include "utils.hpp"
#include <GLFW/glfw3.h>
class AppInteractive {
class SwapChain;
class VulkanWindow {
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);
@ -19,8 +38,6 @@ private:
void initVulkan();
void mainLoop();
void createLogicalDevice(const std::vector<const char *> &deviceExtensions);
void getQueues();
@ -31,10 +48,6 @@ private:
void createSynchronizationObjects();
void drawFrame();
void updateUniformBuffer() const;
void destroySynchronizationObjects();
void cleanup();
@ -67,9 +80,10 @@ private:
VkQueue mPresentQueue = 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 mSemaphoreFramebufferDrawn = VK_NULL_HANDLE;