Presenting from swapchain induces segfault

1

Currently trying to create a main loop which presents images from a swapchain:

void loop(struct Context *ctx, void(*loopfn)(struct Context*)) {
    VkQueue queue;
    vkGetDeviceQueue(ctx->objects.device, 0, 0, &queue);

    while (!glfwWindowShouldClose(ctx->window)) {
        VkResult fenceStatus = vkGetFenceStatus(ctx->objects.device, ctx->objects.fence);

        if (fenceStatus == VK_SUCCESS) {
            // Reset fence and command buffer

            if (vkQueuePresentKHR(queue, &ctx->objects.presentInfo) != VK_SUCCESS) {
                fprintf(stderr, "Failed to present image\n");
                break;
            }

            loopfn(ctx);
        } else if (fenceStatus == VK_ERROR_DEVICE_LOST) {
            fprintf(stderr, "Device lost\n");
            break;
        }
    }
}

void loopfn(struct Context *ctx) {
    // Clear color image
}

int main() {
    struct Context *ctx = init(500, 500, "Test");
    if (!ctx)
        return -1;

    loop(ctx, loopfn);

    deinit(ctx);
    return 0;
}

However, it looks like I'm using the swapchain extension in an incorrect way, which translates to the following error message from the validation layers, immediately followed by a segmentation fault:

UNASSIGNED-CoreValidation-DrawState-SwapchainInvalidImage(ERROR / SPEC): msgNum: 0 - vkQueuePresentKHR: Swapchain image index too large (0). There are only 0 images in this swapchain.
    Objects: 1
       [0] 0x7, type: 1000001000, name: (null)
Validation(ERROR): msg_code: 0:  [ UNASSIGNED-CoreValidation-DrawState-SwapchainInvalidImage ] Object: 0x7 (Type = 27) | vkQueuePresentKHR: Swapchain image index too large (0). There are only 0 images in this swapchain.

My init function is very long and I don't know if it'd be very useful to put it in here, but what I'm essentially doing (and which does not work) is:

  • Creating the swapchain as follows:

    VkSwapchainCreateInfoKHR swapchainInfo =
        { .sType                 = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR
        , .pNext                 = NULL
        , .flags                 = 0x00000000
        , .surface               = ctx->objects.surface
        , .minImageCount         = surfaceCapabilities.minImageCount
        , .imageFormat           = pSurfaceFormats[0].format
        , .imageColorSpace       = pSurfaceFormats[0].colorSpace
        , .imageExtent           = surfaceCapabilities.currentExtent
        , .imageArrayLayers      = 1
        , .imageUsage            = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT
        , .imageSharingMode      = VK_SHARING_MODE_EXCLUSIVE
        , .queueFamilyIndexCount = 1
        , .pQueueFamilyIndices   = NULL
        , .preTransform          = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
        , .compositeAlpha        = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
        , .presentMode           = VK_PRESENT_MODE_MAILBOX_KHR
        , .clipped               = VK_TRUE
        , .oldSwapchain          = VK_NULL_HANDLE
        };
    
    if (vkCreateSwapchainKHR(ctx->objects.device, &swapchainInfo, NULL, &ctx->objects.swapchain) != VK_SUCCESS)
        goto failure;
    
  • Creating the color image and depth image. Both VkImageCreateInfo structures have a pointer to the following struct as their pNext value:

    VkImageSwapchainCreateInfoKHR swapchainImageInfo =
        { .sType     = VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR
        , .pNext     = NULL
        , .swapchain = ctx->objects.swapchain
        };
    
  • Allocating memory for both images, then attempting to bind them to the swapchain as follows:

    VkBindImageMemorySwapchainInfoKHR colorSwapchainBindInfo =
        { .sType      = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR
        , .pNext      = NULL
        , .swapchain  = ctx->objects.swapchain
        , .imageIndex = 0
        };
    
    VkBindImageMemoryInfo colorMemBindInfo =
        { .sType        = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO
        , .pNext        = &colorSwapchainBindInfo
        , .image        = ctx->objects.colorImage
        , .memory       = ctx->objects.colorMemory
        , .memoryOffset = 0
        };
    
    // Pretty much the same thing for the depth image
    
    VkBindImageMemoryInfo memBindInfos[] = { colorMemBindInfo, depthMemBindInfo };
    
    if (vkBindImageMemory2(ctx->objects.device, 2, memBindInfos) != VK_SUCCESS)
        goto failure;
    
  • Performing the two needed image layout transitions (from VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_GENERAL)

  • Creating the two needed image views, a framebuffer and a VkPresentInfoKHR struct

I'd like to know what I'm doing wrong and how to fix it. Feel free to ask for any additional precisions.

Edit: The instance extensions I'm using are VK_KHR_surface and VK_KHR_xcb_surface.

uint32_t count;
const char **extensions = glfwGetRequiredInstanceExtensions(&count);

VkInstanceCreateInfo instanceInfo =
    { .sType                   = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
    , .pNext                   = NULL
    , .flags                   = 0x00000000
    , .pApplicationInfo        = NULL
    , .enabledLayerCount       = 0
    , .ppEnabledLayerNames     = NULL
    , .enabledExtensionCount   = count
    , .ppEnabledExtensionNames = extensions
    };

The only device extension I'm using is VK_KHR_swapchain:

const char *deviceExtensions[] = { "VK_KHR_swapchain" };

VkDeviceCreateInfo deviceInfo =
    { .sType                   = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO
    , .pNext                   = NULL
    , .flags                   = 0x00000000
    , .queueCreateInfoCount    = 1
    , .pQueueCreateInfos       = &queueInfo
    , .enabledLayerCount       = 0
    , .ppEnabledLayerNames     = NULL
    , .enabledExtensionCount   = sizeof(deviceExtensions) / sizeof(deviceExtensions[0])
    , .ppEnabledExtensionNames = deviceExtensions
    , .pEnabledFeatures        = NULL
    };
c
vulkan
asked on Stack Overflow Nov 25, 2018 by Algorythmis • edited Nov 25, 2018 by Algorythmis

1 Answer

3

Right, so this is a XY problem. Seems you are using Vulkan functionality that is inappropriate for your situation. Namely VkImageSwapchainCreateInfoKHR and VkBindImageMemorySwapchainInfoKHR. Based on their descriptions they are only useful for GPU groups. But based on your comments you just want to use the Swapchain the old boring way.

Swapchain needs to create its own images in a special way. That is typically a restriction of the underlying system the Vulkan Swapchain abstracts (such as X, Wayland, GDI, or DXGI). So you would not try to create those images yourself (nor try to bind memory to them), but let vkCreateSwapchain do that for you.

BTW Vulkan swapchain has no use for depth image. It is all color images.

So when the Swapchain is vkCreated you obtain the images it created for you with vkGetSwapchainImagesKHR. The usage is something like:

uint32_t count;
VkResult errco = vkGetSwapchainImagesKHR( dev, swpch, &count, nullptr );
if( errco ) throw "error";

std::vector<VkImage> swapchainImages( count );
errco = vkGetSwapchainImagesKHR( dev, swpch, &count, swapchainImages.data() );
if( errco ) throw "error";

Note: theoretically you may get more images than you asked for with vkCreateSwapchain.

Those images are as good as VkImages obtained regularly with vkCreateImage, except two things:

  • you are not allowed to vkDestroyImage them; they are instead destroyed when you call vkDestroySwapchainKHR.
  • they already have memory bound to them (so you do not need to, nor are you allowed to bind memory to those images).

That should do for introduction, and addressing the immediate problem here.

answered on Stack Overflow Nov 25, 2018 by krOoze

User contributions licensed under CC BY-SA 3.0