Fix support for headless vulkan context

This commit is contained in:
Wunkolo 2023-07-25 13:48:07 -07:00
parent 17101e9bb9
commit d0832ca558
2 changed files with 192 additions and 159 deletions

View file

@ -32,9 +32,14 @@ class RendererVK final : public Renderer {
std::vector<vk::Image> swapchainImages = {}; std::vector<vk::Image> swapchainImages = {};
std::vector<vk::UniqueImageView> swapchainImageViews = {}; std::vector<vk::UniqueImageView> swapchainImageViews = {};
// Per-swapchain-image data // This value is the degree of parallelism to allow multiple frames to be in-flight
// Each vector is `swapchainImageCount` in size // aka: "double-buffer"/"triple-buffering"
std::vector<vk::UniqueCommandBuffer> presentCommandBuffers = {}; // Todo: make this a configuration option
static constexpr usize frameBufferingCount = 3;
// Frame-buffering data
// Each vector is `frameBufferingCount` in size
std::vector<vk::UniqueCommandBuffer> frameCommandBuffers = {};
std::vector<vk::UniqueSemaphore> swapImageFreeSemaphore = {}; std::vector<vk::UniqueSemaphore> swapImageFreeSemaphore = {};
std::vector<vk::UniqueSemaphore> renderFinishedSemaphore = {}; std::vector<vk::UniqueSemaphore> renderFinishedSemaphore = {};
std::vector<vk::UniqueFence> frameFinishedFences = {}; std::vector<vk::UniqueFence> frameFinishedFences = {};
@ -42,7 +47,8 @@ class RendererVK final : public Renderer {
// Recreate the swapchain, possibly re-using the old one in the case of a resize // Recreate the swapchain, possibly re-using the old one in the case of a resize
vk::Result recreateSwapchain(vk::SurfaceKHR surface, vk::Extent2D swapchainExtent); vk::Result recreateSwapchain(vk::SurfaceKHR surface, vk::Extent2D swapchainExtent);
u64 currentFrame = 0; u64 frameBufferingIndex = 0;
public: public:
RendererVK(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs); RendererVK(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
~RendererVK() override; ~RendererVK() override;

View file

@ -155,48 +155,6 @@ vk::Result RendererVK::recreateSwapchain(vk::SurfaceKHR surface, vk::Extent2D sw
Helpers::panic("Error creating acquiring swapchain images: %s\n", vk::to_string(getResult.result).c_str()); Helpers::panic("Error creating acquiring swapchain images: %s\n", vk::to_string(getResult.result).c_str());
} }
// Swapchain Command buffer(s)
vk::CommandBufferAllocateInfo commandBuffersInfo = {};
commandBuffersInfo.commandPool = commandPool.get();
commandBuffersInfo.level = vk::CommandBufferLevel::ePrimary;
commandBuffersInfo.commandBufferCount = swapchainImageCount;
if (auto allocateResult = device->allocateCommandBuffersUnique(commandBuffersInfo); allocateResult.result == vk::Result::eSuccess) {
presentCommandBuffers = std::move(allocateResult.value);
} else {
Helpers::panic("Error allocating command buffer: %s\n", vk::to_string(allocateResult.result).c_str());
}
// Swapchain synchronization primitives
vk::FenceCreateInfo fenceInfo = {};
fenceInfo.flags = vk::FenceCreateFlagBits::eSignaled;
vk::SemaphoreCreateInfo semaphoreInfo = {};
swapImageFreeSemaphore.resize(swapchainImageCount);
renderFinishedSemaphore.resize(swapchainImageCount);
frameFinishedFences.resize(swapchainImageCount);
for (usize i = 0; i < swapchainImageCount; i++) {
if (auto createResult = device->createSemaphoreUnique(semaphoreInfo); createResult.result == vk::Result::eSuccess) {
swapImageFreeSemaphore[i] = std::move(createResult.value);
} else {
Helpers::panic("Error creating 'present-ready' semaphore: %s\n", vk::to_string(createResult.result).c_str());
}
if (auto createResult = device->createSemaphoreUnique(semaphoreInfo); createResult.result == vk::Result::eSuccess) {
renderFinishedSemaphore[i] = std::move(createResult.value);
} else {
Helpers::panic("Error creating 'post-render' semaphore: %s\n", vk::to_string(createResult.result).c_str());
}
if (auto createResult = device->createFenceUnique(fenceInfo); createResult.result == vk::Result::eSuccess) {
frameFinishedFences[i] = std::move(createResult.value);
} else {
Helpers::panic("Error creating 'present-ready' semaphore: %s\n", vk::to_string(createResult.result).c_str());
}
}
return vk::Result::eSuccess; return vk::Result::eSuccess;
} }
@ -208,133 +166,143 @@ RendererVK::~RendererVK() {}
void RendererVK::reset() {} void RendererVK::reset() {}
void RendererVK::display() { void RendererVK::display() {
// Block, on the CPU, to ensure that this swapchain-frame is ready for more work // Block, on the CPU, to ensure that this frame-buffering-frame is ready for more work
if (auto waitResult = device->waitForFences({frameFinishedFences[currentFrame].get()}, true, std::numeric_limits<u64>::max()); if (auto waitResult = device->waitForFences({frameFinishedFences[frameBufferingIndex].get()}, true, std::numeric_limits<u64>::max());
waitResult != vk::Result::eSuccess) { waitResult != vk::Result::eSuccess) {
Helpers::panic("Error waiting on swapchain fence: %s\n", vk::to_string(waitResult).c_str()); Helpers::panic("Error waiting on swapchain fence: %s\n", vk::to_string(waitResult).c_str());
} }
u32 swapchainImageIndex = std::numeric_limits<u32>::max(); // Get the next available swapchain image, and signal the semaphore when it's ready
if (const auto acquireResult = static constexpr u32 swapchainImageInvalid = std::numeric_limits<u32>::max();
device->acquireNextImageKHR(swapchain.get(), std::numeric_limits<u64>::max(), swapImageFreeSemaphore[currentFrame].get(), {}); u32 swapchainImageIndex = swapchainImageInvalid;
acquireResult.result == vk::Result::eSuccess) { if (swapchain) {
swapchainImageIndex = acquireResult.value; if (const auto acquireResult =
} else { device->acquireNextImageKHR(swapchain.get(), std::numeric_limits<u64>::max(), swapImageFreeSemaphore[frameBufferingIndex].get(), {});
switch (acquireResult.result) { acquireResult.result == vk::Result::eSuccess) {
case vk::Result::eSuboptimalKHR: swapchainImageIndex = acquireResult.value;
case vk::Result::eErrorOutOfDateKHR: { } else {
// Surface resized switch (acquireResult.result) {
vk::Extent2D swapchainExtent; case vk::Result::eSuboptimalKHR:
{ case vk::Result::eErrorOutOfDateKHR: {
int windowWidth, windowHeight; // Surface resized
// Block until we have a valid surface-area to present to vk::Extent2D swapchainExtent;
// Usually this is because the window has been minimized {
// Todo: We should still be rendering even without a valid swapchain int windowWidth, windowHeight;
do { // Block until we have a valid surface-area to present to
SDL_Vulkan_GetDrawableSize(targetWindow, &windowWidth, &windowHeight); // Usually this is because the window has been minimized
} while (!windowWidth || !windowHeight); // Todo: We should still be rendering even without a valid swapchain
swapchainExtent.width = windowWidth; do {
swapchainExtent.height = windowHeight; SDL_Vulkan_GetDrawableSize(targetWindow, &windowWidth, &windowHeight);
} while (!windowWidth || !windowHeight);
swapchainExtent.width = windowWidth;
swapchainExtent.height = windowHeight;
}
recreateSwapchain(surface.get(), swapchainExtent);
break;
}
default: {
Helpers::panic("Error acquiring next swapchain image: %s\n", vk::to_string(acquireResult.result).c_str());
} }
recreateSwapchain(surface.get(), swapchainExtent);
break;
}
default: {
Helpers::panic("Error acquiring next swapchain image: %s\n", vk::to_string(acquireResult.result).c_str());
} }
} }
} }
vk::UniqueCommandBuffer& presentCommandBuffer = presentCommandBuffers.at(currentFrame); vk::UniqueCommandBuffer& frameCommandBuffer = frameCommandBuffers.at(frameBufferingIndex);
vk::CommandBufferBeginInfo beginInfo = {}; vk::CommandBufferBeginInfo beginInfo = {};
beginInfo.flags = vk::CommandBufferUsageFlagBits::eSimultaneousUse; beginInfo.flags = vk::CommandBufferUsageFlagBits::eSimultaneousUse;
if (const vk::Result beginResult = presentCommandBuffer->begin(beginInfo); beginResult != vk::Result::eSuccess) { if (const vk::Result beginResult = frameCommandBuffer->begin(beginInfo); beginResult != vk::Result::eSuccess) {
Helpers::panic("Error beginning command buffer recording: %s\n", vk::to_string(beginResult).c_str()); Helpers::panic("Error beginning command buffer recording: %s\n", vk::to_string(beginResult).c_str());
} }
{ {
static const std::array<float, 4> presentScopeColor = {{1.0f, 0.0f, 1.0f, 1.0f}}; static const std::array<float, 4> frameScopeColor = {{1.0f, 0.0f, 1.0f, 1.0f}};
Vulkan::DebugLabelScope debugScope(presentCommandBuffer.get(), presentScopeColor, "Present"); Vulkan::DebugLabelScope debugScope(frameCommandBuffer.get(), frameScopeColor, "Frame");
// Prepare for color-clear // Prepare for color-clear
presentCommandBuffer->pipelineBarrier( if (swapchainImageIndex != swapchainImageInvalid) {
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlags(), {}, {}, frameCommandBuffer->pipelineBarrier(
{vk::ImageMemoryBarrier( vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlags(), {}, {},
vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined, {vk::ImageMemoryBarrier(
vk::ImageLayout::eTransferDstOptimal, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, swapchainImages[swapchainImageIndex], vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined,
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1) vk::ImageLayout::eTransferDstOptimal, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, swapchainImages[swapchainImageIndex],
)} vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
); )}
);
presentCommandBuffer->clearColorImage( static const std::array<float, 4> clearColor = {{0.0f, 0.0f, 0.0f, 1.0f}};
swapchainImages[swapchainImageIndex], vk::ImageLayout::eTransferDstOptimal, presentScopeColor, frameCommandBuffer->clearColorImage(
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1) swapchainImages[swapchainImageIndex], vk::ImageLayout::eTransferDstOptimal, clearColor,
);
// Prepare for present
presentCommandBuffer->pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::DependencyFlags(), {}, {},
{vk::ImageMemoryBarrier(
vk::AccessFlagBits::eNone, vk::AccessFlagBits::eColorAttachmentWrite, vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::ePresentSrcKHR, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, swapchainImages[swapchainImageIndex],
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1) vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
)} );
);
// Prepare for present
frameCommandBuffer->pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eColorAttachmentOutput, vk::DependencyFlags(), {}, {},
{vk::ImageMemoryBarrier(
vk::AccessFlagBits::eNone, vk::AccessFlagBits::eColorAttachmentWrite, vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::ePresentSrcKHR, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, swapchainImages[swapchainImageIndex],
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
)}
);
}
} }
if (const vk::Result endResult = presentCommandBuffer->end(); endResult != vk::Result::eSuccess) { if (const vk::Result endResult = frameCommandBuffer->end(); endResult != vk::Result::eSuccess) {
Helpers::panic("Error ending command buffer recording: %s\n", vk::to_string(endResult).c_str()); Helpers::panic("Error ending command buffer recording: %s\n", vk::to_string(endResult).c_str());
} }
vk::SubmitInfo submitInfo = {}; vk::SubmitInfo submitInfo = {};
// Wait for any previous uses of the image image to finish presenting // Wait for any previous uses of the image image to finish presenting
submitInfo.setWaitSemaphores(swapImageFreeSemaphore[currentFrame].get()); if (swapchainImageIndex != swapchainImageInvalid) {
submitInfo.setWaitSemaphores(swapImageFreeSemaphore[frameBufferingIndex].get());
static const vk::PipelineStageFlags waitStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
submitInfo.setWaitDstStageMask(waitStageMask);
}
// Signal when finished // Signal when finished
submitInfo.setSignalSemaphores(renderFinishedSemaphore[currentFrame].get()); submitInfo.setSignalSemaphores(renderFinishedSemaphore[frameBufferingIndex].get());
static const vk::PipelineStageFlags waitStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput; submitInfo.setCommandBuffers(frameCommandBuffer.get());
submitInfo.setWaitDstStageMask(waitStageMask);
submitInfo.setCommandBuffers(presentCommandBuffer.get()); device->resetFences({frameFinishedFences[frameBufferingIndex].get()});
device->resetFences({frameFinishedFences[currentFrame].get()}); if (const vk::Result submitResult = graphicsQueue.submit({submitInfo}, frameFinishedFences[frameBufferingIndex].get());
if (const vk::Result submitResult = graphicsQueue.submit({submitInfo}, frameFinishedFences[currentFrame].get());
submitResult != vk::Result::eSuccess) { submitResult != vk::Result::eSuccess) {
Helpers::panic("Error submitting to graphics queue: %s\n", vk::to_string(submitResult).c_str()); Helpers::panic("Error submitting to graphics queue: %s\n", vk::to_string(submitResult).c_str());
} }
vk::PresentInfoKHR presentInfo = {}; if (swapchainImageIndex != swapchainImageInvalid) {
presentInfo.setWaitSemaphores(renderFinishedSemaphore[currentFrame].get()); vk::PresentInfoKHR presentInfo = {};
presentInfo.setSwapchains(swapchain.get()); presentInfo.setWaitSemaphores(renderFinishedSemaphore[frameBufferingIndex].get());
presentInfo.setImageIndices(swapchainImageIndex); presentInfo.setSwapchains(swapchain.get());
presentInfo.setImageIndices(swapchainImageIndex);
if (const auto presentResult = presentQueue.presentKHR(presentInfo); presentResult == vk::Result::eSuccess) { if (const auto presentResult = presentQueue.presentKHR(presentInfo); presentResult == vk::Result::eSuccess) {
} else { } else {
switch (presentResult) { switch (presentResult) {
case vk::Result::eSuboptimalKHR: case vk::Result::eSuboptimalKHR:
case vk::Result::eErrorOutOfDateKHR: { case vk::Result::eErrorOutOfDateKHR: {
// Surface resized // Surface resized
vk::Extent2D swapchainExtent; vk::Extent2D swapchainExtent;
{ {
int windowWidth, windowHeight; int windowWidth, windowHeight;
SDL_Vulkan_GetDrawableSize(targetWindow, &windowWidth, &windowHeight); SDL_Vulkan_GetDrawableSize(targetWindow, &windowWidth, &windowHeight);
swapchainExtent.width = windowWidth; swapchainExtent.width = windowWidth;
swapchainExtent.height = windowHeight; swapchainExtent.height = windowHeight;
}
recreateSwapchain(surface.get(), swapchainExtent);
break;
}
default: {
Helpers::panic("Error presenting swapchain image: %s\n", vk::to_string(presentResult).c_str());
} }
recreateSwapchain(surface.get(), swapchainExtent);
break;
}
default: {
Helpers::panic("Error presenting swapchain image: %s\n", vk::to_string(presentResult).c_str());
} }
} }
} }
currentFrame = ((currentFrame + 1) % swapchainImageCount); frameBufferingIndex = ((frameBufferingIndex + 1) % frameBufferingCount);
} }
void RendererVK::initGraphicsContext(SDL_Window* window) { void RendererVK::initGraphicsContext(SDL_Window* window) {
@ -365,11 +333,11 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
}; };
// Get any additional extensions that SDL wants as well // Get any additional extensions that SDL wants as well
{ if (targetWindow) {
unsigned int extensionCount = 0; unsigned int extensionCount = 0;
SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr); SDL_Vulkan_GetInstanceExtensions(targetWindow, &extensionCount, nullptr);
std::vector<const char*> sdlInstanceExtensions(extensionCount); std::vector<const char*> sdlInstanceExtensions(extensionCount);
SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, sdlInstanceExtensions.data()); SDL_Vulkan_GetInstanceExtensions(targetWindow, &extensionCount, sdlInstanceExtensions.data());
instanceExtensions.insert(instanceExtensions.end(), sdlInstanceExtensions.begin(), sdlInstanceExtensions.end()); instanceExtensions.insert(instanceExtensions.end(), sdlInstanceExtensions.begin(), sdlInstanceExtensions.end());
} }
@ -411,10 +379,12 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
} }
// Create surface // Create surface
if (VkSurfaceKHR newSurface; SDL_Vulkan_CreateSurface(window, instance.get(), &newSurface)) { if (window) {
surface.reset(newSurface); if (VkSurfaceKHR newSurface; SDL_Vulkan_CreateSurface(window, instance.get(), &newSurface)) {
} else { surface.reset(newSurface);
Helpers::warn("Error creating Vulkan surface"); } else {
Helpers::warn("Error creating Vulkan surface");
}
} }
// Pick physical device // Pick physical device
@ -423,18 +393,20 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
std::vector<vk::PhysicalDevice>::iterator partitionEnd = physicalDevices.end(); std::vector<vk::PhysicalDevice>::iterator partitionEnd = physicalDevices.end();
// Prefer GPUs that can access the surface // Prefer GPUs that can access the surface
const auto surfaceSupport = [this](const vk::PhysicalDevice& physicalDevice) -> bool { if (surface) {
const usize queueCount = physicalDevice.getQueueFamilyProperties().size(); const auto surfaceSupport = [this](const vk::PhysicalDevice& physicalDevice) -> bool {
for (usize queueIndex = 0; queueIndex < queueCount; ++queueIndex) { const usize queueCount = physicalDevice.getQueueFamilyProperties().size();
if (auto supportResult = physicalDevice.getSurfaceSupportKHR(queueIndex, surface.get()); for (usize queueIndex = 0; queueIndex < queueCount; ++queueIndex) {
supportResult.result == vk::Result::eSuccess) { if (auto supportResult = physicalDevice.getSurfaceSupportKHR(queueIndex, surface.get());
return supportResult.value; supportResult.result == vk::Result::eSuccess) {
return supportResult.value;
}
} }
} return false;
return false; };
};
partitionEnd = std::stable_partition(physicalDevices.begin(), partitionEnd, surfaceSupport); partitionEnd = std::stable_partition(physicalDevices.begin(), partitionEnd, surfaceSupport);
}
// Prefer Discrete GPUs // Prefer Discrete GPUs
const auto isDiscrete = [](const vk::PhysicalDevice& physicalDevice) -> bool { const auto isDiscrete = [](const vk::PhysicalDevice& physicalDevice) -> bool {
@ -454,26 +426,32 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
std::vector<vk::DeviceQueueCreateInfo> deviceQueueInfos; std::vector<vk::DeviceQueueCreateInfo> deviceQueueInfos;
{ {
const std::vector<vk::QueueFamilyProperties> queueFamilyProperties = physicalDevice.getQueueFamilyProperties(); const std::vector<vk::QueueFamilyProperties> queueFamilyProperties = physicalDevice.getQueueFamilyProperties();
std::unordered_set<u32> queueFamilyRequests;
// Get present queue family // Get present queue family
for (usize queueFamilyIndex = 0; queueFamilyIndex < queueFamilyProperties.size(); ++queueFamilyIndex) { if (surface) {
if (auto supportResult = physicalDevice.getSurfaceSupportKHR(queueFamilyIndex, surface.get()); for (usize queueFamilyIndex = 0; queueFamilyIndex < queueFamilyProperties.size(); ++queueFamilyIndex) {
supportResult.result == vk::Result::eSuccess) { if (auto supportResult = physicalDevice.getSurfaceSupportKHR(queueFamilyIndex, surface.get());
if (supportResult.value) { supportResult.result == vk::Result::eSuccess) {
presentQueueFamily = queueFamilyIndex; if (supportResult.value) {
break; presentQueueFamily = queueFamilyIndex;
break;
}
} }
} }
queueFamilyRequests.emplace(presentQueueFamily);
} }
static const float queuePriority = 1.0f; static const float queuePriority = 1.0f;
graphicsQueueFamily = findQueueFamily(queueFamilyProperties, vk::QueueFlagBits::eGraphics); graphicsQueueFamily = findQueueFamily(queueFamilyProperties, vk::QueueFlagBits::eGraphics);
queueFamilyRequests.emplace(graphicsQueueFamily);
computeQueueFamily = findQueueFamily(queueFamilyProperties, vk::QueueFlagBits::eCompute); computeQueueFamily = findQueueFamily(queueFamilyProperties, vk::QueueFlagBits::eCompute);
queueFamilyRequests.emplace(computeQueueFamily);
transferQueueFamily = findQueueFamily(queueFamilyProperties, vk::QueueFlagBits::eTransfer); transferQueueFamily = findQueueFamily(queueFamilyProperties, vk::QueueFlagBits::eTransfer);
queueFamilyRequests.emplace(transferQueueFamily);
// Requests a singular queue for each unique queue-family // Requests a singular queue for each unique queue-family
const std::unordered_set<u32> queueFamilyRequests = {presentQueueFamily, graphicsQueueFamily, computeQueueFamily, transferQueueFamily};
for (const u32 queueFamilyIndex : queueFamilyRequests) { for (const u32 queueFamilyIndex : queueFamilyRequests) {
deviceQueueInfos.emplace_back(vk::DeviceQueueCreateInfo({}, queueFamilyIndex, 1, &queuePriority)); deviceQueueInfos.emplace_back(vk::DeviceQueueCreateInfo({}, queueFamilyIndex, 1, &queuePriority));
} }
@ -482,15 +460,31 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
// Create Device // Create Device
vk::DeviceCreateInfo deviceInfo = {}; vk::DeviceCreateInfo deviceInfo = {};
static const char* deviceExtensions[] = { // Device extensions
VK_KHR_SWAPCHAIN_EXTENSION_NAME, std::vector<const char*> deviceExtensions = {
#if defined(__APPLE__) #if defined(__APPLE__)
"VK_KHR_portability_subset", "VK_KHR_portability_subset",
#endif #endif
// VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME // VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME
}; };
deviceInfo.ppEnabledExtensionNames = deviceExtensions;
deviceInfo.enabledExtensionCount = std::size(deviceExtensions); std::unordered_set<std::string> physicalDeviceExtensions;
if (const auto enumerateResult = physicalDevice.enumerateDeviceExtensionProperties(); enumerateResult.result == vk::Result::eSuccess) {
for (const auto& extension : enumerateResult.value) {
physicalDeviceExtensions.insert(extension.extensionName);
}
} else {
Helpers::panic("Error enumerating physical devices extensions: %s\n", vk::to_string(enumerateResult.result).c_str());
}
// Opertional extensions
// Optionally enable the swapchain, to support "headless" rendering
if (physicalDeviceExtensions.contains(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
deviceExtensions.emplace_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
}
deviceInfo.setPEnabledExtensionNames(deviceExtensions);
vk::StructureChain<vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceTimelineSemaphoreFeatures> deviceFeatureChain = {}; vk::StructureChain<vk::PhysicalDeviceFeatures2, vk::PhysicalDeviceTimelineSemaphoreFeatures> deviceFeatureChain = {};
@ -528,6 +522,39 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
} }
// Create swapchain // Create swapchain
if (targetWindow && surface) {
vk::Extent2D swapchainExtent;
{
int windowWidth, windowHeight;
SDL_Vulkan_GetDrawableSize(window, &windowWidth, &windowHeight);
swapchainExtent.width = windowWidth;
swapchainExtent.height = windowHeight;
}
recreateSwapchain(surface.get(), swapchainExtent);
}
// Create frame-buffering data
// Frame-buffering Command buffer(s)
vk::CommandBufferAllocateInfo commandBuffersInfo = {};
commandBuffersInfo.commandPool = commandPool.get();
commandBuffersInfo.level = vk::CommandBufferLevel::ePrimary;
commandBuffersInfo.commandBufferCount = frameBufferingCount;
if (auto allocateResult = device->allocateCommandBuffersUnique(commandBuffersInfo); allocateResult.result == vk::Result::eSuccess) {
frameCommandBuffers = std::move(allocateResult.value);
} else {
Helpers::panic("Error allocating command buffer: %s\n", vk::to_string(allocateResult.result).c_str());
}
// Frame-buffering synchronization primitives
vk::FenceCreateInfo fenceInfo = {};
fenceInfo.flags = vk::FenceCreateFlagBits::eSignaled;
vk::SemaphoreCreateInfo semaphoreInfo = {};
swapImageFreeSemaphore.resize(frameBufferingCount);
renderFinishedSemaphore.resize(frameBufferingCount);
frameFinishedFences.resize(frameBufferingCount);
vk::Extent2D swapchainExtent; vk::Extent2D swapchainExtent;
{ {
int windowWidth, windowHeight; int windowWidth, windowHeight;