blob: ef8d1170fc385de41fd618b212d9d469c614ac48 [file] [log] [blame]
/*
* Copyright 2025 Rive
*/
#include "rive_vk_bootstrap/vulkan_device.hpp"
#include "rive_vk_bootstrap/vulkan_instance.hpp"
#include "rive_vk_bootstrap/vulkan_swapchain.hpp"
#include "logging.hpp"
#include "vulkan_library.hpp"
namespace rive_vkb
{
VulkanSwapchain::VulkanSwapchain(VulkanInstance& instance,
VulkanDevice& device,
rive::rcp<rive::gpu::VulkanContext> vk,
VkSurfaceKHR surface,
const Options& opts) :
Super(instance,
device,
std::move(vk),
{
.initialFrameNumber = opts.initialFrameNumber,
.externalGPUSynchronization = true,
})
{
assert(opts.formatPreferences.size() > 0 &&
"Must request at least one surface format");
assert(opts.presentModePreferences.size() > 0 &&
"Must request at least one present mode");
// Load all of the functions we care about
#define LOAD(name) LOAD_REQUIRED_MEMBER_INSTANCE_FUNC(name, instance);
RIVE_VK_SWAPCHAIN_INSTANCE_COMMANDS(LOAD);
#undef LOAD
// Check the device to see what our best-match image format is
auto surfaceFormat =
findBestFormat(device, surface, opts.formatPreferences);
auto presentMode =
findBestPresentMode(device, surface, opts.presentModePreferences);
VkCompositeAlphaFlagBitsKHR compositeAlphaFlags =
#if defined(__ANDROID__)
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
#else
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
#endif
auto surfaceCaps = device.getSurfaceCapabilities(surface);
VkSwapchainCreateInfoKHR swapchainCreateInfo = {
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.surface = surface,
.minImageCount =
std::max(opts.preferredImageCount, surfaceCaps.minImageCount),
.imageFormat = surfaceFormat.format,
.imageColorSpace = surfaceFormat.colorSpace,
.imageExtent = surfaceCaps.currentExtent,
.imageArrayLayers = 1,
.imageUsage = opts.imageUsageFlags,
.preTransform = surfaceCaps.currentTransform,
.compositeAlpha = compositeAlphaFlags,
.presentMode = presentMode,
.clipped = true,
};
m_width = surfaceCaps.currentExtent.width;
m_height = surfaceCaps.currentExtent.height;
DEFINE_AND_LOAD_INSTANCE_FUNC(vkCreateSwapchainKHR, instance);
VK_CHECK(vkCreateSwapchainKHR(device.vkDevice(),
&swapchainCreateInfo,
nullptr,
&m_swapchain));
m_imageFormat = surfaceFormat.format;
m_imageUsageFlags = opts.imageUsageFlags;
// Get the swapchain images and then build out our internal image data
std::vector<VkImage> vkImages;
{
DEFINE_AND_LOAD_INSTANCE_FUNC(vkGetSwapchainImagesKHR, instance);
uint32_t count;
vkGetSwapchainImagesKHR(device.vkDevice(),
m_swapchain,
&count,
nullptr);
vkImages.resize(count);
vkGetSwapchainImagesKHR(device.vkDevice(),
m_swapchain,
&count,
vkImages.data());
}
m_swapchainImages.resize(vkImages.size());
DEFINE_AND_LOAD_INSTANCE_FUNC(vkCreateImageView, instance);
for (uint32_t i = 0; i < vkImages.size(); i++)
{
m_swapchainImages[i].image = vkImages[i];
VkImageViewCreateInfo viewCreateInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.image = vkImages[i],
.viewType = VK_IMAGE_VIEW_TYPE_2D,
.format = m_imageFormat,
.subresourceRange =
{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.levelCount = 1,
.layerCount = 1,
},
};
vkCreateImageView(device.vkDevice(),
&viewCreateInfo,
nullptr,
&m_swapchainImages[i].view);
}
}
VulkanSwapchain::~VulkanSwapchain()
{
// Don't do anything until everything is flushed through.
m_vkDeviceWaitIdle(vkDevice());
for (auto& data : m_swapchainImages)
{
m_vkDestroyImageView(vkDevice(), data.view, nullptr);
}
m_vkDestroySwapchainKHR(vkDevice(), m_swapchain, nullptr);
}
VkSurfaceFormatKHR VulkanSwapchain::findBestFormat(
VulkanDevice& device,
VkSurfaceKHR surface,
const std::vector<VkSurfaceFormatKHR>& preferences)
{
auto formats = device.getSurfaceFormats(surface);
for (auto& pref : preferences)
{
for (auto& format : formats)
{
if (format.format == pref.format &&
format.colorSpace == pref.colorSpace)
{
return pref;
}
}
}
LOG_ERROR_LINE("Could not find any preferred surface format");
abort();
}
VkPresentModeKHR VulkanSwapchain::findBestPresentMode(
VulkanDevice& device,
VkSurfaceKHR surface,
const std::vector<VkPresentModeKHR>& presentModePreferences)
{
auto modes = device.getSurfacePresentModes(surface);
for (auto& pref : presentModePreferences)
{
for (auto& mode : modes)
{
if (mode == pref)
{
return pref;
}
}
}
LOG_ERROR_LINE("Could not find any preferred present mode");
abort();
}
bool VulkanSwapchain::isFrameStarted() const
{
return m_currentImageIndex < m_swapchainImages.size();
}
void VulkanSwapchain::beginFrame()
{
assert(!isFrameStarted());
// Do the work for the frame synchronization to begin
auto semaphoreToSignal = Super::waitForFenceAndBeginFrame();
// Next, acquire the next image from the swap chain, and signal the
static constexpr auto NO_TIMEOUT = std::numeric_limits<uint64_t>::max();
VK_CHECK(m_vkAcquireNextImageKHR(vkDevice(),
m_swapchain,
NO_TIMEOUT,
semaphoreToSignal,
VK_NULL_HANDLE,
&m_currentImageIndex));
}
void VulkanSwapchain::queueImageCopy(
rive::gpu::vkutil::ImageAccess* inOutLastAccess,
rive::IAABB optPixelReadBounds)
{
queueImageCopy(current().image,
m_imageFormat,
inOutLastAccess,
optPixelReadBounds);
}
void VulkanSwapchain::endFrame(const rive::gpu::vkutil::ImageAccess& lastAccess)
{
assert(isFrameStarted());
auto& swapImage = current();
// Whether or not we attempted to copy from the swapchain we need to
// transition it to the present layout.
swapImage.lastAccess = context()->simpleImageMemoryBarrier(
currentCommandBuffer(),
lastAccess,
{
.pipelineStages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
.accessMask = VK_ACCESS_NONE,
.layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
},
swapImage.image);
// Now that the memory barrier is in the command buffer, we can end the
// frame sync frame.
VkSemaphore waitSemaphore = Super::endFrame();
// Now queue the actual presentation of the swpchain image
VkPresentInfoKHR presentInfo = {
.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &waitSemaphore,
.swapchainCount = 1,
.pSwapchains = &m_swapchain,
.pImageIndices = &m_currentImageIndex,
};
m_vkQueuePresentKHR(graphicsQueue(), &presentInfo);
// This puts us in the !IsFrameStarted() state
m_currentImageIndex = std::numeric_limits<uint32_t>::max();
}
} // namespace rive_vkb