mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-06 22:25:41 +12:00
Fix support for headless vulkan context
This commit is contained in:
parent
17101e9bb9
commit
d0832ca558
2 changed files with 192 additions and 159 deletions
|
@ -32,9 +32,14 @@ class RendererVK final : public Renderer {
|
|||
std::vector<vk::Image> swapchainImages = {};
|
||||
std::vector<vk::UniqueImageView> swapchainImageViews = {};
|
||||
|
||||
// Per-swapchain-image data
|
||||
// Each vector is `swapchainImageCount` in size
|
||||
std::vector<vk::UniqueCommandBuffer> presentCommandBuffers = {};
|
||||
// This value is the degree of parallelism to allow multiple frames to be in-flight
|
||||
// aka: "double-buffer"/"triple-buffering"
|
||||
// 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> renderFinishedSemaphore = {};
|
||||
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
|
||||
vk::Result recreateSwapchain(vk::SurfaceKHR surface, vk::Extent2D swapchainExtent);
|
||||
|
||||
u64 currentFrame = 0;
|
||||
u64 frameBufferingIndex = 0;
|
||||
|
||||
public:
|
||||
RendererVK(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
|
||||
~RendererVK() override;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -208,133 +166,143 @@ RendererVK::~RendererVK() {}
|
|||
void RendererVK::reset() {}
|
||||
|
||||
void RendererVK::display() {
|
||||
// Block, on the CPU, to ensure that this swapchain-frame is ready for more work
|
||||
if (auto waitResult = device->waitForFences({frameFinishedFences[currentFrame].get()}, true, std::numeric_limits<u64>::max());
|
||||
// Block, on the CPU, to ensure that this frame-buffering-frame is ready for more work
|
||||
if (auto waitResult = device->waitForFences({frameFinishedFences[frameBufferingIndex].get()}, true, std::numeric_limits<u64>::max());
|
||||
waitResult != vk::Result::eSuccess) {
|
||||
Helpers::panic("Error waiting on swapchain fence: %s\n", vk::to_string(waitResult).c_str());
|
||||
}
|
||||
|
||||
u32 swapchainImageIndex = std::numeric_limits<u32>::max();
|
||||
if (const auto acquireResult =
|
||||
device->acquireNextImageKHR(swapchain.get(), std::numeric_limits<u64>::max(), swapImageFreeSemaphore[currentFrame].get(), {});
|
||||
acquireResult.result == vk::Result::eSuccess) {
|
||||
swapchainImageIndex = acquireResult.value;
|
||||
} else {
|
||||
switch (acquireResult.result) {
|
||||
case vk::Result::eSuboptimalKHR:
|
||||
case vk::Result::eErrorOutOfDateKHR: {
|
||||
// Surface resized
|
||||
vk::Extent2D swapchainExtent;
|
||||
{
|
||||
int windowWidth, windowHeight;
|
||||
// Block until we have a valid surface-area to present to
|
||||
// Usually this is because the window has been minimized
|
||||
// Todo: We should still be rendering even without a valid swapchain
|
||||
do {
|
||||
SDL_Vulkan_GetDrawableSize(targetWindow, &windowWidth, &windowHeight);
|
||||
} while (!windowWidth || !windowHeight);
|
||||
swapchainExtent.width = windowWidth;
|
||||
swapchainExtent.height = windowHeight;
|
||||
// Get the next available swapchain image, and signal the semaphore when it's ready
|
||||
static constexpr u32 swapchainImageInvalid = std::numeric_limits<u32>::max();
|
||||
u32 swapchainImageIndex = swapchainImageInvalid;
|
||||
if (swapchain) {
|
||||
if (const auto acquireResult =
|
||||
device->acquireNextImageKHR(swapchain.get(), std::numeric_limits<u64>::max(), swapImageFreeSemaphore[frameBufferingIndex].get(), {});
|
||||
acquireResult.result == vk::Result::eSuccess) {
|
||||
swapchainImageIndex = acquireResult.value;
|
||||
} else {
|
||||
switch (acquireResult.result) {
|
||||
case vk::Result::eSuboptimalKHR:
|
||||
case vk::Result::eErrorOutOfDateKHR: {
|
||||
// Surface resized
|
||||
vk::Extent2D swapchainExtent;
|
||||
{
|
||||
int windowWidth, windowHeight;
|
||||
// Block until we have a valid surface-area to present to
|
||||
// Usually this is because the window has been minimized
|
||||
// Todo: We should still be rendering even without a valid swapchain
|
||||
do {
|
||||
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 = {};
|
||||
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());
|
||||
}
|
||||
|
||||
{
|
||||
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
|
||||
presentCommandBuffer->pipelineBarrier(
|
||||
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlags(), {}, {},
|
||||
{vk::ImageMemoryBarrier(
|
||||
vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eTransferDstOptimal, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, swapchainImages[swapchainImageIndex],
|
||||
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
)}
|
||||
);
|
||||
if (swapchainImageIndex != swapchainImageInvalid) {
|
||||
frameCommandBuffer->pipelineBarrier(
|
||||
vk::PipelineStageFlagBits::eAllCommands, vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlags(), {}, {},
|
||||
{vk::ImageMemoryBarrier(
|
||||
vk::AccessFlagBits::eMemoryRead, vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eUndefined,
|
||||
vk::ImageLayout::eTransferDstOptimal, VK_QUEUE_FAMILY_IGNORED, VK_QUEUE_FAMILY_IGNORED, swapchainImages[swapchainImageIndex],
|
||||
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
)}
|
||||
);
|
||||
|
||||
presentCommandBuffer->clearColorImage(
|
||||
swapchainImages[swapchainImageIndex], vk::ImageLayout::eTransferDstOptimal, presentScopeColor,
|
||||
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1)
|
||||
);
|
||||
|
||||
// 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],
|
||||
static const std::array<float, 4> clearColor = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
frameCommandBuffer->clearColorImage(
|
||||
swapchainImages[swapchainImageIndex], vk::ImageLayout::eTransferDstOptimal, clearColor,
|
||||
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());
|
||||
}
|
||||
|
||||
vk::SubmitInfo submitInfo = {};
|
||||
// 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
|
||||
submitInfo.setSignalSemaphores(renderFinishedSemaphore[currentFrame].get());
|
||||
submitInfo.setSignalSemaphores(renderFinishedSemaphore[frameBufferingIndex].get());
|
||||
|
||||
static const vk::PipelineStageFlags waitStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
|
||||
submitInfo.setWaitDstStageMask(waitStageMask);
|
||||
submitInfo.setCommandBuffers(frameCommandBuffer.get());
|
||||
|
||||
submitInfo.setCommandBuffers(presentCommandBuffer.get());
|
||||
device->resetFences({frameFinishedFences[frameBufferingIndex].get()});
|
||||
|
||||
device->resetFences({frameFinishedFences[currentFrame].get()});
|
||||
|
||||
if (const vk::Result submitResult = graphicsQueue.submit({submitInfo}, frameFinishedFences[currentFrame].get());
|
||||
if (const vk::Result submitResult = graphicsQueue.submit({submitInfo}, frameFinishedFences[frameBufferingIndex].get());
|
||||
submitResult != vk::Result::eSuccess) {
|
||||
Helpers::panic("Error submitting to graphics queue: %s\n", vk::to_string(submitResult).c_str());
|
||||
}
|
||||
|
||||
vk::PresentInfoKHR presentInfo = {};
|
||||
presentInfo.setWaitSemaphores(renderFinishedSemaphore[currentFrame].get());
|
||||
presentInfo.setSwapchains(swapchain.get());
|
||||
presentInfo.setImageIndices(swapchainImageIndex);
|
||||
if (swapchainImageIndex != swapchainImageInvalid) {
|
||||
vk::PresentInfoKHR presentInfo = {};
|
||||
presentInfo.setWaitSemaphores(renderFinishedSemaphore[frameBufferingIndex].get());
|
||||
presentInfo.setSwapchains(swapchain.get());
|
||||
presentInfo.setImageIndices(swapchainImageIndex);
|
||||
|
||||
if (const auto presentResult = presentQueue.presentKHR(presentInfo); presentResult == vk::Result::eSuccess) {
|
||||
} else {
|
||||
switch (presentResult) {
|
||||
case vk::Result::eSuboptimalKHR:
|
||||
case vk::Result::eErrorOutOfDateKHR: {
|
||||
// Surface resized
|
||||
vk::Extent2D swapchainExtent;
|
||||
{
|
||||
int windowWidth, windowHeight;
|
||||
SDL_Vulkan_GetDrawableSize(targetWindow, &windowWidth, &windowHeight);
|
||||
swapchainExtent.width = windowWidth;
|
||||
swapchainExtent.height = windowHeight;
|
||||
if (const auto presentResult = presentQueue.presentKHR(presentInfo); presentResult == vk::Result::eSuccess) {
|
||||
} else {
|
||||
switch (presentResult) {
|
||||
case vk::Result::eSuboptimalKHR:
|
||||
case vk::Result::eErrorOutOfDateKHR: {
|
||||
// Surface resized
|
||||
vk::Extent2D swapchainExtent;
|
||||
{
|
||||
int windowWidth, windowHeight;
|
||||
SDL_Vulkan_GetDrawableSize(targetWindow, &windowWidth, &windowHeight);
|
||||
swapchainExtent.width = windowWidth;
|
||||
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) {
|
||||
|
@ -365,11 +333,11 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
|
|||
};
|
||||
|
||||
// Get any additional extensions that SDL wants as well
|
||||
{
|
||||
if (targetWindow) {
|
||||
unsigned int extensionCount = 0;
|
||||
SDL_Vulkan_GetInstanceExtensions(window, &extensionCount, nullptr);
|
||||
SDL_Vulkan_GetInstanceExtensions(targetWindow, &extensionCount, nullptr);
|
||||
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());
|
||||
}
|
||||
|
@ -411,10 +379,12 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
|
|||
}
|
||||
|
||||
// Create surface
|
||||
if (VkSurfaceKHR newSurface; SDL_Vulkan_CreateSurface(window, instance.get(), &newSurface)) {
|
||||
surface.reset(newSurface);
|
||||
} else {
|
||||
Helpers::warn("Error creating Vulkan surface");
|
||||
if (window) {
|
||||
if (VkSurfaceKHR newSurface; SDL_Vulkan_CreateSurface(window, instance.get(), &newSurface)) {
|
||||
surface.reset(newSurface);
|
||||
} else {
|
||||
Helpers::warn("Error creating Vulkan surface");
|
||||
}
|
||||
}
|
||||
|
||||
// Pick physical device
|
||||
|
@ -423,18 +393,20 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
|
|||
std::vector<vk::PhysicalDevice>::iterator partitionEnd = physicalDevices.end();
|
||||
|
||||
// Prefer GPUs that can access the surface
|
||||
const auto surfaceSupport = [this](const vk::PhysicalDevice& physicalDevice) -> bool {
|
||||
const usize queueCount = physicalDevice.getQueueFamilyProperties().size();
|
||||
for (usize queueIndex = 0; queueIndex < queueCount; ++queueIndex) {
|
||||
if (auto supportResult = physicalDevice.getSurfaceSupportKHR(queueIndex, surface.get());
|
||||
supportResult.result == vk::Result::eSuccess) {
|
||||
return supportResult.value;
|
||||
if (surface) {
|
||||
const auto surfaceSupport = [this](const vk::PhysicalDevice& physicalDevice) -> bool {
|
||||
const usize queueCount = physicalDevice.getQueueFamilyProperties().size();
|
||||
for (usize queueIndex = 0; queueIndex < queueCount; ++queueIndex) {
|
||||
if (auto supportResult = physicalDevice.getSurfaceSupportKHR(queueIndex, surface.get());
|
||||
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
|
||||
const auto isDiscrete = [](const vk::PhysicalDevice& physicalDevice) -> bool {
|
||||
|
@ -454,26 +426,32 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
|
|||
std::vector<vk::DeviceQueueCreateInfo> deviceQueueInfos;
|
||||
{
|
||||
const std::vector<vk::QueueFamilyProperties> queueFamilyProperties = physicalDevice.getQueueFamilyProperties();
|
||||
|
||||
std::unordered_set<u32> queueFamilyRequests;
|
||||
// Get present queue family
|
||||
for (usize queueFamilyIndex = 0; queueFamilyIndex < queueFamilyProperties.size(); ++queueFamilyIndex) {
|
||||
if (auto supportResult = physicalDevice.getSurfaceSupportKHR(queueFamilyIndex, surface.get());
|
||||
supportResult.result == vk::Result::eSuccess) {
|
||||
if (supportResult.value) {
|
||||
presentQueueFamily = queueFamilyIndex;
|
||||
break;
|
||||
if (surface) {
|
||||
for (usize queueFamilyIndex = 0; queueFamilyIndex < queueFamilyProperties.size(); ++queueFamilyIndex) {
|
||||
if (auto supportResult = physicalDevice.getSurfaceSupportKHR(queueFamilyIndex, surface.get());
|
||||
supportResult.result == vk::Result::eSuccess) {
|
||||
if (supportResult.value) {
|
||||
presentQueueFamily = queueFamilyIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
queueFamilyRequests.emplace(presentQueueFamily);
|
||||
}
|
||||
|
||||
static const float queuePriority = 1.0f;
|
||||
|
||||
graphicsQueueFamily = findQueueFamily(queueFamilyProperties, vk::QueueFlagBits::eGraphics);
|
||||
queueFamilyRequests.emplace(graphicsQueueFamily);
|
||||
computeQueueFamily = findQueueFamily(queueFamilyProperties, vk::QueueFlagBits::eCompute);
|
||||
queueFamilyRequests.emplace(computeQueueFamily);
|
||||
transferQueueFamily = findQueueFamily(queueFamilyProperties, vk::QueueFlagBits::eTransfer);
|
||||
queueFamilyRequests.emplace(transferQueueFamily);
|
||||
|
||||
// Requests a singular queue for each unique queue-family
|
||||
const std::unordered_set<u32> queueFamilyRequests = {presentQueueFamily, graphicsQueueFamily, computeQueueFamily, transferQueueFamily};
|
||||
|
||||
for (const u32 queueFamilyIndex : queueFamilyRequests) {
|
||||
deviceQueueInfos.emplace_back(vk::DeviceQueueCreateInfo({}, queueFamilyIndex, 1, &queuePriority));
|
||||
}
|
||||
|
@ -482,15 +460,31 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
|
|||
// Create Device
|
||||
vk::DeviceCreateInfo deviceInfo = {};
|
||||
|
||||
static const char* deviceExtensions[] = {
|
||||
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
|
||||
// Device extensions
|
||||
std::vector<const char*> deviceExtensions = {
|
||||
#if defined(__APPLE__)
|
||||
"VK_KHR_portability_subset",
|
||||
#endif
|
||||
// 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 = {};
|
||||
|
||||
|
@ -528,6 +522,39 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
|
|||
}
|
||||
|
||||
// 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;
|
||||
{
|
||||
int windowWidth, windowHeight;
|
||||
|
|
Loading…
Add table
Reference in a new issue