Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 75 additions & 40 deletions attachments/33_vulkan_profiles.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ class HelloTriangleApplication {
void initWindow() {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan Profiles Demo", nullptr, nullptr);
glfwSetWindowUserPointer(window, this);
glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);
Expand Down Expand Up @@ -221,23 +223,11 @@ class HelloTriangleApplication {
}

void cleanupSwapChain() {
colorImageView = nullptr;
colorImage = nullptr;
colorImageMemory = nullptr;

depthImageView = nullptr;
depthImage = nullptr;
depthImageMemory = nullptr;

for (auto& framebuffer : swapChainFramebuffers) {
framebuffer = nullptr;
}
swapChainFramebuffers.clear();
swapChainImageViews.clear();

for (auto& imageView : swapChainImageViews) {
imageView = nullptr;
}

swapChain = nullptr;
// Semaphores tied to swapchain image indices need to be rebuilt on resize
presentCompleteSemaphore.clear();
}

void cleanup() {
Expand Down Expand Up @@ -268,6 +258,13 @@ class HelloTriangleApplication {

createColorResources();
createDepthResources();

// Recreate per-swapchain-image present semaphores after resize
presentCompleteSemaphore.reserve(swapChainImages.size());
vk::SemaphoreCreateInfo semaphoreInfo{};
for (size_t i = 0; i < swapChainImages.size(); ++i) {
presentCompleteSemaphore.push_back(device.createSemaphore(semaphoreInfo));
}
}

void createInstance() {
Expand Down Expand Up @@ -1338,8 +1335,45 @@ class HelloTriangleApplication {
void recordCommandBuffer(uint32_t imageIndex) {
commandBuffers[currentFrame].begin({});

// Transition the swapchain image to the correct layout for rendering
vk::ImageMemoryBarrier imageBarrier{
// Transition the attachments to the correct layouts for dynamic rendering
// 1) Multisampled color attachment image -> ColorAttachmentOptimal
vk::ImageMemoryBarrier colorAttachmentBarrier{
.srcAccessMask = vk::AccessFlagBits::eNone,
.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite,
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eColorAttachmentOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *colorImage,
.subresourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
};

// 2) Depth attachment image -> DepthStencilAttachmentOptimal
vk::ImageMemoryBarrier depthAttachmentBarrier{
.srcAccessMask = vk::AccessFlagBits::eNone,
.dstAccessMask = vk::AccessFlagBits::eDepthStencilAttachmentWrite,
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *depthImage,
.subresourceRange = {
.aspectMask = vk::ImageAspectFlagBits::eDepth,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1
}
};

// 3) Resolve (swapchain) image -> ColorAttachmentOptimal
vk::ImageMemoryBarrier swapchainResolveBarrier{
.srcAccessMask = vk::AccessFlagBits::eNone,
.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite,
.oldLayout = vk::ImageLayout::eUndefined,
Expand All @@ -1358,11 +1392,11 @@ class HelloTriangleApplication {

commandBuffers[currentFrame].pipelineBarrier(
vk::PipelineStageFlagBits::eTopOfPipe,
vk::PipelineStageFlagBits::eColorAttachmentOutput,
vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests,
vk::DependencyFlagBits::eByRegion,
std::array<vk::MemoryBarrier, 0>{},
std::array<vk::BufferMemoryBarrier, 0>{},
std::array<vk::ImageMemoryBarrier, 1>{imageBarrier}
std::array<vk::ImageMemoryBarrier, 3>{colorAttachmentBarrier, depthAttachmentBarrier, swapchainResolveBarrier}
);

// Clear values for color and depth
Expand Down Expand Up @@ -1548,28 +1582,29 @@ class HelloTriangleApplication {
};
queue.submit(submitInfo, *inFlightFences[currentFrame]);

const vk::PresentInfoKHR presentInfoKHR{
.waitSemaphoreCount = 1,
.pWaitSemaphores = &*presentCompleteSemaphore[imageIndex],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this example still have renderFinishedSemaphores, but also have these presentCompleteSemaphore array that seems to server the same purpose, but with a slightly misleading name (It seems to be used more as a "rendering complete/present-ready" indicator, rather than a "present complete" indicator, the latter of which I'm not sure is possible to measure using semaphores in current Vulkan, which is what lead me to look closely at this code).

The Android file below seems to still use renderFinishedSemaphores in the manner that fits its name for what appears to be the same steps renderFinishedSemaphores is used here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'opps' is the why :) I made a bug. Thanks for the report, I'll address it.

.swapchainCount = 1,
.pSwapchains = &*swapChain,
.pImageIndices = &imageIndex
};

vk::Result result;
try {
result = queue.presentKHR(presentInfoKHR);
} catch (vk::OutOfDateKHRError&) {
result = vk::Result::eErrorOutOfDateKHR;
}

if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) {
framebufferResized = false;
recreateSwapChain();
} else if (result != vk::Result::eSuccess) {
throw std::runtime_error("failed to present swap chain image!");
const vk::PresentInfoKHR presentInfoKHR{
.waitSemaphoreCount = 1,
.pWaitSemaphores = &*presentCompleteSemaphore[imageIndex],
.swapchainCount = 1,
.pSwapchains = &*swapChain,
.pImageIndices = &imageIndex
};
auto result = queue.presentKHR(presentInfoKHR);
if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) {
framebufferResized = false;
recreateSwapChain();
} else if (result != vk::Result::eSuccess) {
throw std::runtime_error("failed to present swap chain image!");
}
} catch (const vk::SystemError& e) {
if (e.code().value() == static_cast<int>(vk::Result::eErrorOutOfDateKHR)) {
recreateSwapChain();
return;
} else {
throw;
}
}

currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}

Expand Down
82 changes: 52 additions & 30 deletions attachments/34_android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ class HelloTriangleApplication {
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan Cross-Platform", nullptr, nullptr);
glfwSetWindowUserPointer(window, this);
glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);
Expand Down Expand Up @@ -1177,7 +1178,7 @@ class HelloTriangleApplication {
// Create synchronization objects
void createSyncObjects() {
imageAvailableSemaphores.reserve(MAX_FRAMES_IN_FLIGHT);
renderFinishedSemaphores.reserve(MAX_FRAMES_IN_FLIGHT);
renderFinishedSemaphores.reserve(swapChainImages.size());
inFlightFences.reserve(MAX_FRAMES_IN_FLIGHT);

vk::SemaphoreCreateInfo semaphoreInfo{};
Expand All @@ -1187,22 +1188,21 @@ class HelloTriangleApplication {

for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
imageAvailableSemaphores.push_back(device.createSemaphore(semaphoreInfo));
renderFinishedSemaphores.push_back(device.createSemaphore(semaphoreInfo));
inFlightFences.push_back(device.createFence(fenceInfo));
}

for (size_t i = 0; i < swapChainImages.size(); i++) {
renderFinishedSemaphores.push_back(device.createSemaphore(semaphoreInfo));
}
}

// Clean up swap chain
void cleanupSwapChain() {
for (auto& framebuffer : swapChainFramebuffers) {
framebuffer = nullptr;
}
swapChainFramebuffers.clear();
swapChainImageViews.clear();

for (auto& imageView : swapChainImageViews) {
imageView = nullptr;
}

swapChain = nullptr;
// Semaphores tied to swapchain image indices need to be rebuilt on resize
renderFinishedSemaphores.clear();
}

// Record command buffer
Expand Down Expand Up @@ -1281,47 +1281,69 @@ class HelloTriangleApplication {
.commandBufferCount = 1,
.pCommandBuffers = &*commandBuffers[currentFrame],
.signalSemaphoreCount = 1,
.pSignalSemaphores = &*renderFinishedSemaphores[currentFrame]
.pSignalSemaphores = &*renderFinishedSemaphores[imageIndex]
};
queue.submit(submitInfo, *inFlightFences[currentFrame]);

const vk::PresentInfoKHR presentInfoKHR{
.waitSemaphoreCount = 1,
.pWaitSemaphores = &*renderFinishedSemaphores[currentFrame],
.swapchainCount = 1,
.pSwapchains = &*swapChain,
.pImageIndices = &imageIndex
};

vk::Result result;
try {
result = queue.presentKHR(presentInfoKHR);
} catch (vk::OutOfDateKHRError&) {
result = vk::Result::eErrorOutOfDateKHR;
}
const vk::PresentInfoKHR presentInfoKHR{
.waitSemaphoreCount = 1,
.pWaitSemaphores = &*renderFinishedSemaphores[imageIndex],
.swapchainCount = 1,
.pSwapchains = &*swapChain,
.pImageIndices = &imageIndex
};

if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) {
framebufferResized = false;
recreateSwapChain();
} else if (result != vk::Result::eSuccess) {
throw std::runtime_error("Failed to present swap chain image");
vk::Result result = queue.presentKHR(presentInfoKHR);

if (result == vk::Result::eErrorOutOfDateKHR || result == vk::Result::eSuboptimalKHR || framebufferResized) {
framebufferResized = false;
recreateSwapChain();
} else if (result != vk::Result::eSuccess) {
throw std::runtime_error("Failed to present swap chain image");
}
} catch (const vk::SystemError& e) {
if (e.code().value() == static_cast<int>(vk::Result::eErrorOutOfDateKHR)) {
recreateSwapChain();
return;
} else {
throw;
}
}

currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}

// Recreate swap chain
void recreateSwapChain() {
#if !PLATFORM_ANDROID
// On desktop, wait until the framebuffer has a non-zero size (e.g., when window is minimized)
int width = 0, height = 0;
if (window) {
glfwGetFramebufferSize(window, &width, &height);
while (width == 0 || height == 0) {
glfwGetFramebufferSize(window, &width, &height);
glfwWaitEvents();
}
}
#endif
// Wait for device to finish operations
device.waitIdle();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note per recent spec updates, waiting for idle on the queue used for presentation is sufficient.

Also, it would be best if VK_EXT_swapchain_maintenance1's present fence were used here instead.

Is there a tutorial yet for smooth resize? I.e., using oldSwapchain to create the new swapchain and deferring cleanup until pending swaps have reached a point where it's safe to delete the old sawpchain(s)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, we should open 3 issues you presented here:
1.) remove not needed device.waitIdle();
2.) use swapchain_maintenance1
3.) request to create a smooth resize tutorial.

The first two will require making changes to the tutorial itself as we don't want to just change the code without making sure the tutorial explains what needs to be done to be correct. The last will require a full tutorial which takes more time and planning; however, it should be done as we do see the request from time to time to give a sample on smooth resizing.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need to take into account that VK_EXT_swapchain_maintenance1 isn't universally supported, and the KHR variant even less so. So would prob. need to be optional for now.


// Clean up old swap chain
cleanupSwapChain();

// Create new swap chain
// Create new swap chain and dependent resources
createSwapChain();
createImageViews();
createFramebuffers();

// Recreate per-swapchain-image present semaphores for presenting
renderFinishedSemaphores.reserve(swapChainImages.size());
vk::SemaphoreCreateInfo semaphoreInfo{};
for (size_t i = 0; i < swapChainImages.size(); ++i) {
renderFinishedSemaphores.push_back(device.createSemaphore(semaphoreInfo));
}
}

// Get required extensions
Expand Down
Loading