2022-06-15 00:09:51 +00:00
|
|
|
#include "vulkan_swapchain.hpp"
|
|
|
|
#include "vulkan/vulkan_structs.hpp"
|
2023-02-23 17:00:06 +00:00
|
|
|
#include <thread>
|
2022-06-15 00:09:51 +00:00
|
|
|
|
|
|
|
namespace Vulkan
|
|
|
|
{
|
|
|
|
|
|
|
|
Swapchain::Swapchain(vk::Device device_, vk::PhysicalDevice physical_device_, vk::Queue queue_, vk::SurfaceKHR surface_, vk::CommandPool command_pool_)
|
|
|
|
: surface(surface_),
|
|
|
|
command_pool(command_pool_),
|
|
|
|
physical_device(physical_device_),
|
|
|
|
queue(queue_)
|
|
|
|
{
|
|
|
|
device = device_;
|
|
|
|
create_render_pass();
|
|
|
|
}
|
|
|
|
|
|
|
|
Swapchain::~Swapchain()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Swapchain::set_vsync(bool new_setting)
|
|
|
|
{
|
|
|
|
if (new_setting == vsync)
|
|
|
|
return false;
|
|
|
|
|
2023-02-10 22:03:05 +00:00
|
|
|
vsync = new_setting;
|
2022-06-15 00:09:51 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Swapchain::create_render_pass()
|
|
|
|
{
|
|
|
|
auto attachment_description = vk::AttachmentDescription{}
|
|
|
|
.setFormat(vk::Format::eB8G8R8A8Unorm)
|
|
|
|
.setSamples(vk::SampleCountFlagBits::e1)
|
|
|
|
.setLoadOp(vk::AttachmentLoadOp::eClear)
|
|
|
|
.setStoreOp(vk::AttachmentStoreOp::eStore)
|
|
|
|
.setStencilLoadOp(vk::AttachmentLoadOp::eDontCare)
|
|
|
|
.setStencilStoreOp(vk::AttachmentStoreOp::eStore)
|
|
|
|
.setInitialLayout(vk::ImageLayout::eUndefined)
|
|
|
|
.setFinalLayout(vk::ImageLayout::ePresentSrcKHR);
|
|
|
|
|
|
|
|
auto attachment_reference = vk::AttachmentReference{}
|
|
|
|
.setAttachment(0)
|
|
|
|
.setLayout(vk::ImageLayout::eColorAttachmentOptimal);
|
|
|
|
|
|
|
|
std::array<vk::SubpassDependency, 2> subpass_dependency{};
|
|
|
|
subpass_dependency[0]
|
|
|
|
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
|
|
|
|
.setDstSubpass(0)
|
|
|
|
.setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
|
|
|
|
.setSrcAccessMask(vk::AccessFlagBits(0))
|
|
|
|
.setDstStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
|
|
|
|
.setDstAccessMask(vk::AccessFlagBits::eColorAttachmentWrite);
|
|
|
|
subpass_dependency[1]
|
|
|
|
.setSrcSubpass(VK_SUBPASS_EXTERNAL)
|
|
|
|
.setDstSubpass(0)
|
|
|
|
.setSrcStageMask(vk::PipelineStageFlagBits::eColorAttachmentOutput)
|
|
|
|
.setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
|
|
|
|
.setDstStageMask(vk::PipelineStageFlagBits::eFragmentShader)
|
|
|
|
.setDstAccessMask(vk::AccessFlagBits::eShaderRead);
|
|
|
|
|
|
|
|
|
|
|
|
auto subpass_description = vk::SubpassDescription{}
|
|
|
|
.setColorAttachments(attachment_reference)
|
|
|
|
.setPipelineBindPoint(vk::PipelineBindPoint::eGraphics);
|
|
|
|
|
|
|
|
auto render_pass_create_info = vk::RenderPassCreateInfo{}
|
|
|
|
.setSubpasses(subpass_description)
|
|
|
|
.setDependencies(subpass_dependency)
|
|
|
|
.setAttachments(attachment_description);
|
|
|
|
|
|
|
|
render_pass = device.createRenderPassUnique(render_pass_create_info);
|
|
|
|
}
|
|
|
|
|
2023-01-24 23:33:46 +00:00
|
|
|
bool Swapchain::recreate(int new_width, int new_height)
|
2022-06-15 00:09:51 +00:00
|
|
|
{
|
|
|
|
device.waitIdle();
|
2023-01-25 20:03:21 +00:00
|
|
|
return create(num_swapchain_images, new_width, new_height);
|
2022-06-15 00:09:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vk::Image Swapchain::get_image()
|
|
|
|
{
|
|
|
|
return imageviewfbs[current_swapchain_image].image;
|
|
|
|
}
|
|
|
|
|
2023-01-25 20:03:21 +00:00
|
|
|
bool Swapchain::create(unsigned int desired_num_swapchain_images, int new_width, int new_height)
|
2022-06-15 00:09:51 +00:00
|
|
|
{
|
|
|
|
frames.clear();
|
|
|
|
imageviewfbs.clear();
|
|
|
|
|
|
|
|
auto surface_capabilities = physical_device.getSurfaceCapabilitiesKHR(surface);
|
|
|
|
|
2023-01-25 20:03:21 +00:00
|
|
|
if (surface_capabilities.minImageCount > desired_num_swapchain_images)
|
|
|
|
num_swapchain_images = surface_capabilities.minImageCount;
|
2022-06-15 00:09:51 +00:00
|
|
|
else
|
2023-01-25 20:03:21 +00:00
|
|
|
num_swapchain_images = desired_num_swapchain_images;
|
2022-06-15 00:09:51 +00:00
|
|
|
|
|
|
|
extents = surface_capabilities.currentExtent;
|
|
|
|
|
2023-01-24 23:33:46 +00:00
|
|
|
if (new_width > 0 && new_height > 0)
|
|
|
|
{
|
|
|
|
// No buffer is allocated for surface yet
|
|
|
|
extents.width = new_width;
|
|
|
|
extents.height = new_height;
|
|
|
|
}
|
|
|
|
else if (extents.width < 1 || extents.height < 1)
|
2022-06-15 00:09:51 +00:00
|
|
|
{
|
|
|
|
// Surface is likely hidden
|
|
|
|
printf("Extents too small.\n");
|
2023-01-24 23:33:46 +00:00
|
|
|
swapchain_object.reset();
|
2022-06-15 00:09:51 +00:00
|
|
|
return false;
|
|
|
|
}
|
2023-02-18 23:25:23 +00:00
|
|
|
else if (extents.width > 8192 || extents.height > 8192)
|
|
|
|
{
|
|
|
|
extents.width = 512;
|
|
|
|
extents.height = 512;
|
|
|
|
}
|
2022-06-15 00:09:51 +00:00
|
|
|
|
|
|
|
auto swapchain_create_info = vk::SwapchainCreateInfoKHR{}
|
2023-01-25 20:03:21 +00:00
|
|
|
.setMinImageCount(num_swapchain_images)
|
2022-06-15 00:09:51 +00:00
|
|
|
.setImageFormat(vk::Format::eB8G8R8A8Unorm)
|
|
|
|
.setImageExtent(extents)
|
|
|
|
.setImageColorSpace(vk::ColorSpaceKHR::eSrgbNonlinear)
|
|
|
|
.setImageSharingMode(vk::SharingMode::eExclusive)
|
|
|
|
.setImageUsage(vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferSrc)
|
|
|
|
.setCompositeAlpha(vk::CompositeAlphaFlagBitsKHR::eOpaque)
|
|
|
|
.setClipped(true)
|
|
|
|
.setPresentMode(vsync ? vk::PresentModeKHR::eFifo : vk::PresentModeKHR::eImmediate)
|
|
|
|
.setSurface(surface)
|
|
|
|
.setImageArrayLayers(1);
|
|
|
|
|
2023-01-24 23:33:46 +00:00
|
|
|
if (swapchain_object)
|
|
|
|
swapchain_create_info.setOldSwapchain(swapchain_object.get());
|
2022-06-15 00:09:51 +00:00
|
|
|
|
2023-01-24 23:33:46 +00:00
|
|
|
swapchain_object = device.createSwapchainKHRUnique(swapchain_create_info);
|
|
|
|
if (!swapchain_object)
|
|
|
|
return false;
|
2022-06-15 00:09:51 +00:00
|
|
|
|
2023-01-24 23:33:46 +00:00
|
|
|
auto swapchain_images = device.getSwapchainImagesKHR(swapchain_object.get());
|
2023-01-25 20:03:21 +00:00
|
|
|
vk::CommandBufferAllocateInfo command_buffer_allocate_info(command_pool, vk::CommandBufferLevel::ePrimary, max_latency);
|
2022-06-15 00:09:51 +00:00
|
|
|
auto command_buffers = device.allocateCommandBuffersUnique(command_buffer_allocate_info);
|
|
|
|
|
2023-01-25 20:03:21 +00:00
|
|
|
if (imageviewfbs.size() > num_swapchain_images)
|
|
|
|
num_swapchain_images = imageviewfbs.size();
|
2022-06-15 00:09:51 +00:00
|
|
|
|
2023-01-25 20:03:21 +00:00
|
|
|
frames.resize(max_latency);
|
|
|
|
imageviewfbs.resize(num_swapchain_images);
|
2022-06-15 00:09:51 +00:00
|
|
|
|
|
|
|
vk::FenceCreateInfo fence_create_info(vk::FenceCreateFlagBits::eSignaled);
|
|
|
|
|
2023-01-25 20:03:21 +00:00
|
|
|
for (int i = 0; i < max_latency; i++)
|
2022-06-15 00:09:51 +00:00
|
|
|
{
|
|
|
|
// Create frame queue resources
|
|
|
|
auto &frame = frames[i];
|
|
|
|
frame.command_buffer = std::move(command_buffers[i]);
|
|
|
|
frame.fence = device.createFenceUnique(fence_create_info);
|
|
|
|
frame.acquire = device.createSemaphoreUnique({});
|
|
|
|
frame.complete = device.createSemaphoreUnique({});
|
|
|
|
}
|
|
|
|
current_frame = 0;
|
|
|
|
|
2023-01-25 20:03:21 +00:00
|
|
|
for (unsigned int i = 0; i < num_swapchain_images; i++)
|
2022-06-15 00:09:51 +00:00
|
|
|
{
|
|
|
|
// Create resources associated with swapchain images
|
|
|
|
auto &image = imageviewfbs[i];
|
|
|
|
image.image = swapchain_images[i];
|
|
|
|
auto image_view_create_info = vk::ImageViewCreateInfo{}
|
|
|
|
.setImage(swapchain_images[i])
|
|
|
|
.setViewType(vk::ImageViewType::e2D)
|
|
|
|
.setFormat(vk::Format::eB8G8R8A8Unorm)
|
|
|
|
.setComponents(vk::ComponentMapping())
|
|
|
|
.setSubresourceRange(vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
|
|
|
|
image.image_view = device.createImageViewUnique(image_view_create_info);
|
|
|
|
|
|
|
|
auto framebuffer_create_info = vk::FramebufferCreateInfo{}
|
|
|
|
.setAttachments(image.image_view.get())
|
|
|
|
.setWidth(extents.width)
|
|
|
|
.setHeight(extents.height)
|
|
|
|
.setLayers(1)
|
|
|
|
.setRenderPass(render_pass.get());
|
|
|
|
image.framebuffer = device.createFramebufferUnique(framebuffer_create_info);
|
|
|
|
}
|
|
|
|
|
2023-01-24 23:33:46 +00:00
|
|
|
device.waitIdle();
|
|
|
|
|
2022-06-15 00:09:51 +00:00
|
|
|
current_swapchain_image = 0;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Swapchain::begin_frame()
|
|
|
|
{
|
2023-01-24 23:33:46 +00:00
|
|
|
if (!swapchain_object || extents.width < 1 || extents.height < 1)
|
2022-06-15 00:09:51 +00:00
|
|
|
{
|
|
|
|
printf ("Extents too small\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto &frame = frames[current_frame];
|
|
|
|
|
|
|
|
auto result = device.waitForFences(frame.fence.get(), true, 33000000);
|
|
|
|
if (result != vk::Result::eSuccess)
|
|
|
|
{
|
|
|
|
printf("Failed fence\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-01-24 23:33:46 +00:00
|
|
|
auto result_value = device.acquireNextImageKHR(swapchain_object.get(), 33000000, frame.acquire.get());
|
2022-06-15 00:09:51 +00:00
|
|
|
if (result_value.result == vk::Result::eErrorOutOfDateKHR ||
|
|
|
|
result_value.result == vk::Result::eSuboptimalKHR)
|
|
|
|
{
|
2023-01-24 23:33:46 +00:00
|
|
|
printf("Out of date\n");
|
2022-06-15 00:09:51 +00:00
|
|
|
recreate();
|
|
|
|
return begin_frame();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result_value.result != vk::Result::eSuccess)
|
|
|
|
{
|
2023-01-24 23:33:46 +00:00
|
|
|
printf("Timeout waiting for frame. Running too slow.\n");
|
2022-06-15 00:09:51 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
current_swapchain_image = result_value.value;
|
|
|
|
|
2023-01-25 20:03:21 +00:00
|
|
|
device.resetFences(frame.fence.get());
|
|
|
|
|
2022-06-15 00:09:51 +00:00
|
|
|
vk::CommandBufferBeginInfo command_buffer_begin_info(vk::CommandBufferUsageFlags{vk::CommandBufferUsageFlagBits::eOneTimeSubmit});
|
|
|
|
frame.command_buffer->begin(command_buffer_begin_info);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-02-23 17:00:06 +00:00
|
|
|
void Swapchain::set_max_frame_rate(double frame_rate)
|
|
|
|
{
|
|
|
|
throttle.set_frame_rate(frame_rate);
|
|
|
|
}
|
|
|
|
|
2023-01-25 20:03:21 +00:00
|
|
|
bool Swapchain::end_frame()
|
2022-06-15 00:09:51 +00:00
|
|
|
{
|
|
|
|
auto &frame = frames[current_frame];
|
|
|
|
frame.command_buffer->end();
|
|
|
|
|
|
|
|
vk::PipelineStageFlags flags = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
|
|
|
vk::SubmitInfo submit_info(
|
|
|
|
frame.acquire.get(),
|
|
|
|
flags,
|
|
|
|
frame.command_buffer.get(),
|
|
|
|
frame.complete.get());
|
|
|
|
|
|
|
|
queue.submit(submit_info, frame.fence.get());
|
|
|
|
|
|
|
|
auto present_info = vk::PresentInfoKHR{}
|
|
|
|
.setWaitSemaphores(frames[current_frame].complete.get())
|
2023-01-24 23:33:46 +00:00
|
|
|
.setSwapchains(swapchain_object.get())
|
2022-06-15 00:09:51 +00:00
|
|
|
.setImageIndices(current_swapchain_image);
|
|
|
|
|
2023-02-23 17:00:06 +00:00
|
|
|
if (throttle.max_frame_rate != 0.0)
|
|
|
|
{
|
|
|
|
auto remaining = throttle.remaining();
|
|
|
|
if (remaining < -throttle.frame_duration_us / 10)
|
|
|
|
throttle.reset();
|
|
|
|
else if (remaining.count() > 0)
|
|
|
|
{
|
|
|
|
queue.waitIdle();
|
|
|
|
throttle.wait_for_frame();
|
|
|
|
throttle.advance();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-15 00:09:51 +00:00
|
|
|
auto result = queue.presentKHR(present_info);
|
|
|
|
|
2023-01-25 20:03:21 +00:00
|
|
|
current_frame = (current_frame + 1) % max_latency;
|
2022-06-15 00:09:51 +00:00
|
|
|
|
|
|
|
if (result != vk::Result::eSuccess)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
vk::Framebuffer Swapchain::get_framebuffer()
|
|
|
|
{
|
|
|
|
return imageviewfbs[current_swapchain_image].framebuffer.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
vk::CommandBuffer &Swapchain::get_cmd()
|
|
|
|
{
|
|
|
|
return frames[current_frame].command_buffer.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Swapchain::begin_render_pass()
|
|
|
|
{
|
|
|
|
vk::ClearColorValue colorval;
|
|
|
|
colorval.setFloat32({ 0.0f, 0.0f, 0.0f, 1.0f });
|
|
|
|
vk::ClearValue value;
|
|
|
|
value.setColor(colorval);
|
|
|
|
|
|
|
|
auto render_pass_begin_info = vk::RenderPassBeginInfo{}
|
|
|
|
.setRenderPass(render_pass.get())
|
|
|
|
.setFramebuffer(imageviewfbs[current_swapchain_image].framebuffer.get())
|
|
|
|
.setRenderArea(vk::Rect2D({}, extents))
|
|
|
|
.setClearValues(value);
|
|
|
|
get_cmd().beginRenderPass(render_pass_begin_info, vk::SubpassContents::eInline);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Swapchain::end_render_pass()
|
|
|
|
{
|
|
|
|
get_cmd().endRenderPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int Swapchain::get_current_frame()
|
|
|
|
{
|
|
|
|
return current_frame;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Swapchain::wait_on_frame(int frame_num)
|
|
|
|
{
|
|
|
|
auto result = device.waitForFences(frames[frame_num].fence.get(), true, 33000000);
|
|
|
|
return (result == vk::Result::eSuccess);
|
|
|
|
}
|
|
|
|
|
|
|
|
vk::Extent2D Swapchain::get_extents()
|
|
|
|
{
|
|
|
|
return extents;
|
|
|
|
}
|
|
|
|
|
|
|
|
vk::RenderPass &Swapchain::get_render_pass()
|
|
|
|
{
|
|
|
|
return render_pass.get();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int Swapchain::get_num_frames()
|
|
|
|
{
|
2023-01-25 20:03:21 +00:00
|
|
|
return max_latency;
|
2022-06-15 00:09:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Vulkan
|