mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-13 17:49:47 +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::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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue