Successfully cleared the screen. created synchronization objects for rendering and presentation

This commit is contained in:
connellpaxton 2024-01-21 22:36:35 -05:00
parent c393e0cdb1
commit 4365cc45d1
10 changed files with 228 additions and 19 deletions

View File

@ -4,7 +4,6 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "-DDEBUG") set(CMAKE_CXX_FLAGS "-DDEBUG")
set(CMAKE_CXX_FLAGS "-fsanitize=address -g -DDEBUG -D_DEBUG")
project(Pleascach) project(Pleascach)

View File

@ -2,6 +2,8 @@
#include <Renderer/Pipeline.hpp> #include <Renderer/Pipeline.hpp>
#include <util/log.hpp>
CommandBuffer::CommandBuffer(vk::Device dev, u32 queue_family) { CommandBuffer::CommandBuffer(vk::Device dev, u32 queue_family) {
/* (For now) allow command buffers to be individually recycled */ /* (For now) allow command buffers to be individually recycled */
auto pool_info = vk::CommandPoolCreateInfo { auto pool_info = vk::CommandPoolCreateInfo {
@ -18,7 +20,6 @@ CommandBuffer::CommandBuffer(vk::Device dev, u32 queue_family) {
.level = vk::CommandBufferLevel::ePrimary, .level = vk::CommandBufferLevel::ePrimary,
.commandBufferCount = 1, .commandBufferCount = 1,
}; };
command_buffer = dev.allocateCommandBuffers(alloc_info)[0]; command_buffer = dev.allocateCommandBuffers(alloc_info)[0];
} }
@ -26,7 +27,6 @@ CommandBuffer::CommandBuffer(vk::Device dev, u32 queue_family) {
void CommandBuffer::begin() { void CommandBuffer::begin() {
auto begin_info = vk::CommandBufferBeginInfo{ auto begin_info = vk::CommandBufferBeginInfo{
.flags = static_cast<vk::CommandBufferUsageFlags>(0), .flags = static_cast<vk::CommandBufferUsageFlags>(0),
.pInheritanceInfo = nullptr,
}; };
command_buffer.begin(begin_info); command_buffer.begin(begin_info);
@ -50,8 +50,10 @@ void CommandBuffer::end() {
void CommandBuffer::recycle() { void CommandBuffer::recycle() {
command_buffer.reset(); command_buffer.reset();
} }
void CommandBuffer::cleanup(vk::Device dev) { void CommandBuffer::cleanup(vk::Device dev) {
dev.freeCommandBuffers(command_pool, command_buffer);
dev.destroyCommandPool(command_pool); dev.destroyCommandPool(command_pool);
} }

View File

@ -29,4 +29,8 @@ struct CommandBuffer {
vk::CommandBuffer command_buffer; vk::CommandBuffer command_buffer;
vk::CommandPool command_pool; vk::CommandPool command_pool;
operator vk::CommandBuffer* () {
return &command_buffer;
}
}; };

View File

@ -145,8 +145,6 @@ Renderer::Renderer(Window& win) : win(win) {
Log::error("Failed to get surface from window\n"); Log::error("Failed to get surface from window\n");
} }
swapchain = std::make_unique<Swapchain>(dev, surface, win.getDimensions());
queue = dev.getQueue(queue_family, 0); queue = dev.getQueue(queue_family, 0);
@ -185,7 +183,7 @@ Renderer::Renderer(Window& win) : win(win) {
.memoryTypeIndex = mem::choose_heap(phys_dev, depth_mem_reqs, vk::MemoryPropertyFlagBits::eDeviceLocal), .memoryTypeIndex = mem::choose_heap(phys_dev, depth_mem_reqs, vk::MemoryPropertyFlagBits::eDeviceLocal),
}; };
auto depth_alloc = dev.allocateMemory(depth_alloc_info); depth_alloc = dev.allocateMemory(depth_alloc_info);
dev.bindImageMemory(depth_image, depth_alloc, 0); dev.bindImageMemory(depth_image, depth_alloc, 0);
auto depth_view_info = vk::ImageViewCreateInfo { auto depth_view_info = vk::ImageViewCreateInfo {
@ -208,29 +206,101 @@ Renderer::Renderer(Window& win) : win(win) {
}; };
depth_image_view = dev.createImageView(depth_view_info); depth_image_view = dev.createImageView(depth_view_info);
render_fence = dev.createFence(vk::FenceCreateInfo { .flags = vk::FenceCreateFlagBits::eSignaled });
image_wait_semaphore = dev.createSemaphore(vk::SemaphoreCreateInfo{});
render_wait_semaphore = dev.createSemaphore(vk::SemaphoreCreateInfo{});
render_pass = std::make_unique<RenderPass>(dev);
swapchain = std::make_unique<Swapchain>(dev, surface, win.getDimensions(), *render_pass, depth_image_view);
command_buffer = std::make_unique<CommandBuffer>(dev, queue_family);
} }
void Renderer::draw() { void Renderer::draw() {
Log::info("draw() called \n");
dev.waitForFences(render_fence, true, UINT64_MAX);
dev.resetFences(render_fence);
/* check if the swapchain is still good (no resize) */ /* check if the swapchain is still good (no resize) */
auto image_ret = dev.acquireNextImageKHR(*swapchain, UINT64_MAX); auto image_ret = dev.acquireNextImageKHR(*swapchain, UINT64_MAX, image_wait_semaphore);
if (image_ret.result == vk::Result::eErrorOutOfDateKHR) { if (image_ret.result == vk::Result::eErrorOutOfDateKHR) {
swapchain->recreate(win.getDimensions()); swapchain->recreate(win.getDimensions());
} }
current_image_idx = image_ret.value; current_image_idx = image_ret.value;
/* prepare command buffer for recording commands */
command_buffer->recycle(); command_buffer->recycle();
command_buffer->begin(); command_buffer->begin();
vk::ClearValue clear_values[] = {
vk::ClearColorValue(1.0f, 0.0f, 1.0f, 1.0f),
vk::ClearDepthStencilValue {.depth = 1.0f}
};
/* use renderpass to transform images from unspecified layout to a presentable one while clearing */
auto render_pass_info = vk::RenderPassBeginInfo {
.renderPass = *render_pass,
.framebuffer = swapchain->framebuffers[current_image_idx],
.renderArea = {
.offset = { 0, 0 },
.extent = swapchain->extent,
},
.clearValueCount = std::size(clear_values),
.pClearValues = clear_values,
};
auto viewport = vk::Viewport{
.x = 0.0f,
.y = 0.0f,
.width = static_cast<float>(swapchain->extent.width),
.height = static_cast<float>(swapchain->extent.height),
.minDepth = 0.0f,
.maxDepth = 1.0f,
};
auto scissor = vk::Rect2D{
.offset = {0, 0},
.extent = swapchain->extent,
};
/* no secondary command buffers (yet), so contents are passed inline */
command_buffer->command_buffer.beginRenderPass(render_pass_info, vk::SubpassContents::eInline);
command_buffer->command_buffer.setViewport(0, viewport);
command_buffer->command_buffer.setScissor(0, scissor);
command_buffer->command_buffer.endRenderPass();
command_buffer->end(); command_buffer->end();
vk::PipelineStageFlags stage_flags = vk::PipelineStageFlagBits::eColorAttachmentOutput;
/* submit our command buffer to the queue */
auto submit_info = vk::SubmitInfo{
.waitSemaphoreCount = 1,
.pWaitSemaphores = &image_wait_semaphore,
.pWaitDstStageMask = &stage_flags,
.commandBufferCount = 1,
.pCommandBuffers = &command_buffer->command_buffer,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &render_wait_semaphore,
};
queue.submit(submit_info, render_fence);
} }
void Renderer::present() { void Renderer::present() {
Log::info("present() called \n");
auto present_info = vk::PresentInfoKHR{ auto present_info = vk::PresentInfoKHR{
.waitSemaphoreCount = 1,
.pWaitSemaphores = &render_wait_semaphore,
.swapchainCount = 1, .swapchainCount = 1,
.pSwapchains = &swapchain->operator vk::SwapchainKHR &(), .pSwapchains = &swapchain->operator vk::SwapchainKHR & (),
.pImageIndices = &current_image_idx, .pImageIndices = &current_image_idx,
}; };
@ -239,6 +309,7 @@ void Renderer::present() {
break; break;
case vk::Result::eSuboptimalKHR: case vk::Result::eSuboptimalKHR:
case vk::Result::eErrorOutOfDateKHR: case vk::Result::eErrorOutOfDateKHR:
Log::info("Recreating swapchain");
swapchain->recreate(win.getDimensions()); swapchain->recreate(win.getDimensions());
break; break;
default: default:
@ -250,9 +321,13 @@ void Renderer::present() {
Renderer::~Renderer() { Renderer::~Renderer() {
dev.destroyImage(depth_image); dev.destroyImage(depth_image);
dev.destroyImageView(depth_image_view); dev.destroyImageView(depth_image_view);
dev.freeMemory(depth_alloc);
swapchain.reset(); swapchain.reset();
dev.waitIdle(); dev.waitIdle();
command_buffer->cleanup(dev);
render_pass->cleanup(dev);
dev.destroy(); dev.destroy();
instance.destroySurfaceKHR(surface); instance.destroySurfaceKHR(surface);
instance.destroy(); instance.destroy();

View File

@ -7,6 +7,7 @@
#include <Renderer/Swapchain.hpp> #include <Renderer/Swapchain.hpp>
#include <Renderer/CommandBuffer.hpp> #include <Renderer/CommandBuffer.hpp>
#include <Renderer/RenderPass.hpp>
struct Window; struct Window;
@ -23,6 +24,8 @@ struct Renderer {
vk::Instance instance; vk::Instance instance;
vk::Device dev; vk::Device dev;
vk::Fence render_fence;
vk::Semaphore image_wait_semaphore, render_wait_semaphore;
vk::SurfaceKHR surface; vk::SurfaceKHR surface;
std::unique_ptr<Swapchain> swapchain; std::unique_ptr<Swapchain> swapchain;
@ -30,8 +33,11 @@ struct Renderer {
vk::Queue queue; vk::Queue queue;
std::unique_ptr<CommandBuffer> command_buffer; std::unique_ptr<CommandBuffer> command_buffer;
std::unique_ptr<RenderPass> render_pass;
uint32_t current_image_idx; uint32_t current_image_idx;
vk::Image depth_image; vk::Image depth_image;
vk::ImageView depth_image_view; vk::ImageView depth_image_view;
vk::DeviceMemory depth_alloc;
}; };

View File

@ -1,10 +1,12 @@
#include <Renderer/Swapchain.hpp> #include <Renderer/Swapchain.hpp>
Swapchain::Swapchain(vk::Device& dev, const vk::SurfaceKHR& surface, const vk::Extent2D& extent) : dev(dev), surface(surface) { Swapchain::Swapchain(vk::Device& dev, const vk::SurfaceKHR& surface, const vk::Extent2D& extent, RenderPass render_pass, vk::ImageView depth_image_view)
: dev(dev), surface(surface), render_pass(render_pass), depth_image_view(depth_image_view) {
create(extent); create(extent);
} }
void Swapchain::create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchain) { void Swapchain::create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchain) {
this->extent = extent;
auto format = vk::Format::eB8G8R8A8Unorm; auto format = vk::Format::eB8G8R8A8Unorm;
@ -33,8 +35,9 @@ void Swapchain::create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchai
images = dev.getSwapchainImagesKHR(swapchain); images = dev.getSwapchainImagesKHR(swapchain);
views.resize(images.size()); views.resize(images.size());
for(size_t i = 0; i < views.size(); i++) { framebuffers.resize(images.size());
auto color_image_info = vk::ImageViewCreateInfo { for (size_t i = 0; i < views.size(); i++) {
auto color_image_info = vk::ImageViewCreateInfo{
.image = images[i], .image = images[i],
.viewType = vk::ImageViewType::e2D, .viewType = vk::ImageViewType::e2D,
.format = format, .format = format,
@ -52,8 +55,19 @@ void Swapchain::create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchai
.layerCount = 1, .layerCount = 1,
} }
}; };
views[i] = dev.createImageView(color_image_info); views[i] = dev.createImageView(color_image_info);
vk::ImageView attachments[] = { views[i], depth_image_view };
auto framebuffer_info = vk::FramebufferCreateInfo {
.renderPass = render_pass,
.attachmentCount = static_cast<uint32_t>(std::size(attachments)),
.pAttachments = attachments,
.width = extent.width,
.height = extent.height,
.layers = 1,
};
framebuffers[i] = dev.createFramebuffer(framebuffer_info);
} }
} }
@ -61,10 +75,12 @@ void Swapchain::create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchai
void Swapchain::recreate(const vk::Extent2D& extent) { void Swapchain::recreate(const vk::Extent2D& extent) {
dev.waitIdle(); dev.waitIdle();
cleanup(); cleanup();
create(extent); create(extent, swapchain);
} }
void Swapchain::cleanup() { void Swapchain::cleanup() {
for(auto& fb : framebuffers)
dev.destroyFramebuffer(fb);
dev.destroySwapchainKHR(swapchain); dev.destroySwapchainKHR(swapchain);
} }

View File

@ -3,8 +3,12 @@
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS #define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
#include <vulkan/vulkan.hpp> #include <vulkan/vulkan.hpp>
#include <Renderer/RenderPass.hpp>
struct Swapchain { struct Swapchain {
Swapchain(vk::Device& dev, const vk::SurfaceKHR& surface, const vk::Extent2D& extent); Swapchain(vk::Device& dev, const vk::SurfaceKHR& surface, const vk::Extent2D& extent, RenderPass render_pass, vk::ImageView depth_image_view);
vk::SwapchainKHR swapchain; vk::SwapchainKHR swapchain;
inline operator vk::SwapchainKHR& () { inline operator vk::SwapchainKHR& () {
return swapchain; return swapchain;
@ -14,6 +18,12 @@ struct Swapchain {
vk::SurfaceKHR surface; vk::SurfaceKHR surface;
std::vector<vk::Image> images; std::vector<vk::Image> images;
std::vector<vk::ImageView> views; std::vector<vk::ImageView> views;
std::vector<vk::Framebuffer> framebuffers;
RenderPass render_pass;
vk::ImageView depth_image_view;
vk::Extent2D extent;
void create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchain = nullptr); void create(const vk::Extent2D& extent, vk::SwapchainKHR old_swapchain = nullptr);
void recreate(const vk::Extent2D& extent); void recreate(const vk::Extent2D& extent);

View File

@ -3,13 +3,11 @@
#include <Renderer/Renderer.hpp> #include <Renderer/Renderer.hpp>
#include <util/log.hpp> #include <util/log.hpp>
#include <iostream> #include <iostream>
int main() { int main(int argc, char* argv[]) {
try { try {
Window win("Test", 256, 512); Window win(argv[0], 256, 512);
auto in = win.getInput(); auto in = win.getInput();
Renderer ren(win); Renderer ren(win);
@ -23,7 +21,6 @@ int main() {
switch (event.tag) { switch (event.tag) {
case InputEvent::Tag::RESIZE: case InputEvent::Tag::RESIZE:
Log::info("Event Processed: Resized to %dx%d\n", event.resize.width, event.resize.height); Log::info("Event Processed: Resized to %dx%d\n", event.resize.width, event.resize.height);
win.setDimensions(event.resize.width, event.resize.width);
/* no need to have a resize() function in the renderer, b/c swapchain images will be /* no need to have a resize() function in the renderer, b/c swapchain images will be
* automatically marked out-of-date, and recreation will be triggered in our code * automatically marked out-of-date, and recreation will be triggered in our code
*/ */
@ -36,6 +33,9 @@ int main() {
break; break;
} }
} }
ren.draw();
ren.present();
} }
} catch (const std::string& e) { } catch (const std::string& e) {

81
renderer/RenderPass.cpp Normal file
View File

@ -0,0 +1,81 @@
#include <Renderer/RenderPass.hpp>
RenderPass::RenderPass(vk::Device dev, vk::Format image_format, vk::Format depth_format) {
/* transform color image from UNDEFINED format to presentable one for rendering */
auto color_attach_desc = vk::AttachmentDescription {
.format = image_format,
.samples = vk::SampleCountFlagBits::e1,
/* since its set to clear, it clears the initial image to the specified
clear color instead of loading the data */
.loadOp = vk::AttachmentLoadOp::eClear,
/* we care about the data after the end of the renderpass, so store */
.storeOp = vk::AttachmentStoreOp::eStore,
/* for the depth stencil buffer, we care about neither */
.stencilLoadOp = vk::AttachmentLoadOp::eDontCare,
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
.initialLayout = vk::ImageLayout::eUndefined,
.finalLayout = vk::ImageLayout::ePresentSrcKHR,
};
auto color_attach_ref = vk::AttachmentReference { .attachment = 0, .layout = vk::ImageLayout::eColorAttachmentOptimal };
auto depth_attach_desc = vk::AttachmentDescription{
.format = depth_format,
.samples = vk::SampleCountFlagBits::e1,
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eDontCare,
.stencilLoadOp = vk::AttachmentLoadOp::eClear,
.stencilStoreOp = vk::AttachmentStoreOp::eDontCare,
.initialLayout = vk::ImageLayout::eUndefined,
.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal,
};
auto depth_attach_ref = vk::AttachmentReference{ .attachment = 1, .layout = vk::ImageLayout::eDepthStencilAttachmentOptimal };
/* only one subpass for now, uses both attachments */
auto subpass_desc = vk::SubpassDescription {
.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
.colorAttachmentCount = 1,
.pColorAttachments = &color_attach_ref,
.pDepthStencilAttachment = &depth_attach_ref,
};
/* designates producer and consumer of the image to position subpass */
auto color_dep = vk::SubpassDependency{
.srcSubpass = vk::SubpassExternal,
.dstSubpass = 0,
.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput,
.dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput,
.srcAccessMask = vk::AccessFlagBits::eNone,
.dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite,
};
auto depth_dep = vk::SubpassDependency {
.srcSubpass = vk::SubpassExternal,
.dstSubpass = 0,
.srcStageMask = vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests,
.dstStageMask = vk::PipelineStageFlagBits::eEarlyFragmentTests | vk::PipelineStageFlagBits::eLateFragmentTests,
.srcAccessMask = vk::AccessFlagBits::eNone,
.dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite,
};
vk::AttachmentDescription descs[] = { color_attach_desc, depth_attach_desc };
vk::SubpassDependency deps[] = { color_dep, depth_dep};
vk::SubpassDescription subpasses[] = {subpass_desc};
auto pass_info = vk::RenderPassCreateInfo {
.attachmentCount = static_cast<uint32_t>(std::size(descs)),
.pAttachments = descs,
.subpassCount = static_cast<uint32_t>(std::size(subpasses)),
.pSubpasses = subpasses,
.dependencyCount = static_cast<uint32_t>(std::size(deps)),
.pDependencies = deps,
};
render_pass = dev.createRenderPass(pass_info);
}
void RenderPass::cleanup(vk::Device dev) {
dev.destroyRenderPass(render_pass);
}

16
renderer/RenderPass.hpp Normal file
View File

@ -0,0 +1,16 @@
#pragma once
#define VULKAN_HPP_NO_STRUCT_CONSTRUCTORS
#include <vulkan/vulkan.hpp>
struct RenderPass {
vk::RenderPass render_pass;
RenderPass(vk::Device dev, vk::Format image_format = vk::Format::eB8G8R8A8Unorm, vk::Format depth_format = vk::Format::eD16Unorm);
void cleanup(vk::Device dev);
operator vk::RenderPass&() {
return render_pass;
}
};