DiligentEngine的API是D3d11和D3D12风格的,vulkan也被封装成了这种风格的API。 在了解Diligent Engine是如何对vulkan进行封装之前,我准备先学习下Vulkan。知乎funchun的编程指南是中文版,英文不好,准备先看一版中文版,回头再去研习其他的内容。

1.Vulkan编程指南阅读摘要

1.1 Vulkan SDK

安装完成之后,安装目录有如下文件。在阅读编程指南的过程中,我并未采用指南中的demo code来进行研究,而是使用的SDK自带的demo进行学习。

1.2 Vulkan SDK Demo

  • validation layers

        /*
    * This is info for a temp callback to use during CreateInstance.
    * After the instance is created, we use the instance-based
    * function to register the final callback.
    */
    VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info;
    if (demo->validate) {
    // VK_EXT_debug_utils style
    dbg_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
    dbg_messenger_create_info.pNext = NULL;
    dbg_messenger_create_info.flags = ;
    dbg_messenger_create_info.messageSeverity =
    VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
    dbg_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
    VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
    VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
    dbg_messenger_create_info.pfnUserCallback = debug_messenger_callback;
    dbg_messenger_create_info.pUserData = demo;
    inst_info.pNext = &dbg_messenger_create_info;
    }
      if (demo->validate) {
    // Setup VK_EXT_debug_utils function pointers always (we use them for
    // debug labels and names).
    demo->CreateDebugUtilsMessengerEXT =
    (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(demo->inst, "vkCreateDebugUtilsMessengerEXT");
    demo->DestroyDebugUtilsMessengerEXT =
    (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(demo->inst, "vkDestroyDebugUtilsMessengerEXT");
    demo->SubmitDebugUtilsMessageEXT =
    (PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(demo->inst, "vkSubmitDebugUtilsMessageEXT");
    demo->CmdBeginDebugUtilsLabelEXT =
    (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(demo->inst, "vkCmdBeginDebugUtilsLabelEXT");
    demo->CmdEndDebugUtilsLabelEXT =
    (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(demo->inst, "vkCmdEndDebugUtilsLabelEXT");
    demo->CmdInsertDebugUtilsLabelEXT =
    (PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(demo->inst, "vkCmdInsertDebugUtilsLabelEXT");
    demo->SetDebugUtilsObjectNameEXT =
    (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(demo->inst, "vkSetDebugUtilsObjectNameEXT");
    if (NULL == demo->CreateDebugUtilsMessengerEXT || NULL == demo->DestroyDebugUtilsMessengerEXT ||
    NULL == demo->SubmitDebugUtilsMessageEXT || NULL == demo->CmdBeginDebugUtilsLabelEXT ||
    NULL == demo->CmdEndDebugUtilsLabelEXT || NULL == demo->CmdInsertDebugUtilsLabelEXT ||
    NULL == demo->SetDebugUtilsObjectNameEXT) {
    ERR_EXIT("GetProcAddr: Failed to init VK_EXT_debug_utils\n", "GetProcAddr: Failure");
    } err = demo->CreateDebugUtilsMessengerEXT(demo->inst, &dbg_messenger_create_info, NULL, &demo->dbg_messenger);
    switch (err) {
    case VK_SUCCESS:
    break;
    case VK_ERROR_OUT_OF_HOST_MEMORY:
    ERR_EXIT("CreateDebugUtilsMessengerEXT: out of host memory\n", "CreateDebugUtilsMessengerEXT Failure");
    break;
    default:
    ERR_EXIT("CreateDebugUtilsMessengerEXT: unknown failure\n", "CreateDebugUtilsMessengerEXT Failure");
    break;
    }
    }
  • instance extensions
  • CreateVulkanInstance
     const VkApplicationInfo app = {
    .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
    .pNext = NULL,
    .pApplicationName = APP_SHORT_NAME,
    .applicationVersion = ,
    .pEngineName = APP_SHORT_NAME,
    .engineVersion = ,
    .apiVersion = VK_API_VERSION_1_0,
    };
    VkInstanceCreateInfo inst_info = {
    .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
    .pNext = NULL,
    .pApplicationInfo = &app,
    .enabledLayerCount = demo->enabled_layer_count,
    .ppEnabledLayerNames = (const char *const *)instance_validation_layers,
    .enabledExtensionCount = demo->enabled_extension_count,
    .ppEnabledExtensionNames = (const char *const *)demo->extension_names,
    }; /*
    * This is info for a temp callback to use during CreateInstance.
    * After the instance is created, we use the instance-based
    * function to register the final callback.
    */
    VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info;
    if (demo->validate) {
    // VK_EXT_debug_utils style
    dbg_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
    dbg_messenger_create_info.pNext = NULL;
    dbg_messenger_create_info.flags = ;
    dbg_messenger_create_info.messageSeverity =
    VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
    dbg_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
    VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
    VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
    dbg_messenger_create_info.pfnUserCallback = debug_messenger_callback;
    dbg_messenger_create_info.pUserData = demo;
    inst_info.pNext = &dbg_messenger_create_info;
    } uint32_t gpu_count; err = vkCreateInstance(&inst_info, NULL, &demo->inst);
  • vkEnumeratePhysicalDevices

        /* Make initial call to query gpu_count, then second call for gpu info*/
    err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, NULL);
    assert(!err); if (gpu_count > ) {
    VkPhysicalDevice *physical_devices = malloc(sizeof(VkPhysicalDevice) * gpu_count);
    err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, physical_devices);
    assert(!err);
    /* For cube demo we just grab the first physical device */
    demo->gpu = physical_devices[];
    free(physical_devices);
    } else {
    ERR_EXIT(
    "vkEnumeratePhysicalDevices reported zero accessible devices.\n\n"
    "Do you have a compatible Vulkan installable client driver (ICD) installed?\n"
    "Please look at the Getting Started guide for additional information.\n",
    "vkEnumeratePhysicalDevices Failure");
    }
        vkGetPhysicalDeviceProperties(demo->gpu, &demo->gpu_props);
    
        /* Call with NULL data to get count */
    vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_family_count, NULL);
    assert(demo->queue_family_count >= ); demo->queue_props = (VkQueueFamilyProperties *)malloc(demo->queue_family_count * sizeof(VkQueueFamilyProperties));
    vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_family_count, demo->queue_props);
      // Query fine-grained feature support for this device.
    // If app has specific feature requirements it should check supported
    // features based on this query
    VkPhysicalDeviceFeatures physDevFeatures;
    vkGetPhysicalDeviceFeatures(demo->gpu, &physDevFeatures);
  • device extensions需要找到对应的 VK_KHR_SWAPCHAIN_EXTENSION_NAME,这个是一个交换链的扩展
  •     /* Look for device extensions */
    uint32_t device_extension_count = ;
    VkBool32 swapchainExtFound = ;
    demo->enabled_extension_count = ;
    memset(demo->extension_names, , sizeof(demo->extension_names)); err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL, &device_extension_count, NULL);
    assert(!err); if (device_extension_count > ) {
    VkExtensionProperties *device_extensions = malloc(sizeof(VkExtensionProperties) * device_extension_count);
    err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL, &device_extension_count, device_extensions);
    assert(!err); for (uint32_t i = ; i < device_extension_count; i++) {
    if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, device_extensions[i].extensionName)) {
    swapchainExtFound = ;
    demo->extension_names[demo->enabled_extension_count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
    }
    assert(demo->enabled_extension_count < );
    } if (demo->VK_KHR_incremental_present_enabled) {
    // Even though the user "enabled" the extension via the command
    // line, we must make sure that it's enumerated for use with the
    // device. Therefore, disable it here, and re-enable it again if
    // enumerated.
    demo->VK_KHR_incremental_present_enabled = false;
    for (uint32_t i = ; i < device_extension_count; i++) {
    if (!strcmp(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, device_extensions[i].extensionName)) {
    demo->extension_names[demo->enabled_extension_count++] = VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME;
    demo->VK_KHR_incremental_present_enabled = true;
    DbgMsg("VK_KHR_incremental_present extension enabled\n");
    }
    assert(demo->enabled_extension_count < );
    }
    if (!demo->VK_KHR_incremental_present_enabled) {
    DbgMsg("VK_KHR_incremental_present extension NOT AVAILABLE\n");
    }
    } if (demo->VK_GOOGLE_display_timing_enabled) {
    // Even though the user "enabled" the extension via the command
    // line, we must make sure that it's enumerated for use with the
    // device. Therefore, disable it here, and re-enable it again if
    // enumerated.
    demo->VK_GOOGLE_display_timing_enabled = false;
    for (uint32_t i = ; i < device_extension_count; i++) {
    if (!strcmp(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, device_extensions[i].extensionName)) {
    demo->extension_names[demo->enabled_extension_count++] = VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME;
    demo->VK_GOOGLE_display_timing_enabled = true;
    DbgMsg("VK_GOOGLE_display_timing extension enabled\n");
    }
    assert(demo->enabled_extension_count < );
    }
    if (!demo->VK_GOOGLE_display_timing_enabled) {
    DbgMsg("VK_GOOGLE_display_timing extension NOT AVAILABLE\n");
    }
    } free(device_extensions);
    } if (!swapchainExtFound) {
    ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find the " VK_KHR_SWAPCHAIN_EXTENSION_NAME
    " extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\n"
    "Please look at the Getting Started guide for additional information.\n",
    "vkCreateInstance Failure");
    }
  • 凡事带有KHR结尾的函数,都是vulkan对于制定平台的一些扩展, 下面的代码创建一个窗口表面,当然如果你不需要展示图像,这个表面不是必须的。
// Create a WSI surface for the window:
#if defined(VK_USE_PLATFORM_WIN32_KHR)
VkWin32SurfaceCreateInfoKHR createInfo;
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
createInfo.pNext = NULL;
createInfo.flags = ;
createInfo.hinstance = demo->connection;
createInfo.hwnd = demo->window; err = vkCreateWin32SurfaceKHR(demo->inst, &createInfo, NULL, &demo->surface);
  • vkCreateDevice

        VkResult U_ASSERT_ONLY err;
    float queue_priorities[] = {0.0};
    VkDeviceQueueCreateInfo queues[];
    queues[].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    queues[].pNext = NULL;
    queues[].queueFamilyIndex = demo->graphics_queue_family_index;
    queues[].queueCount = ;
    queues[].pQueuePriorities = queue_priorities;
    queues[].flags = ; VkDeviceCreateInfo device = {
    .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
    .pNext = NULL,
    .queueCreateInfoCount = ,
    .pQueueCreateInfos = queues,
    .enabledLayerCount = ,
    .ppEnabledLayerNames = NULL,
    .enabledExtensionCount = demo->enabled_extension_count,
    .ppEnabledExtensionNames = (const char *const *)demo->extension_names,
    .pEnabledFeatures = NULL, // If specific features are required, pass them in here
    };
    if (demo->separate_present_queue) {
    queues[].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
    queues[].pNext = NULL;
    queues[].queueFamilyIndex = demo->present_queue_family_index;
    queues[].queueCount = ;
    queues[].pQueuePriorities = queue_priorities;
    queues[].flags = ;
    device.queueCreateInfoCount = ;
    }
    err = vkCreateDevice(demo->gpu, &device, NULL, &demo->device);
    assert(!err);
  • Get the list of VkFormat's that are supported:

      uint32_t formatCount;
    err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface, &formatCount, NULL);
    assert(!err);
    VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
    err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface, &formatCount, surfFormats);
    assert(!err);
    // If the format list includes just one entry of VK_FORMAT_UNDEFINED,
    // the surface has no preferred format. Otherwise, at least one
    // supported format will be returned.
    if (formatCount == && surfFormats[].format == VK_FORMAT_UNDEFINED) {
    demo->format = VK_FORMAT_B8G8R8A8_UNORM;
    } else {
    assert(formatCount >= );
    demo->format = surfFormats[].format;
    }
    demo->color_space = surfFormats[].colorSpace;
    free(surfFormats);
  • 同步
    // Create semaphores to synchronize acquiring presentable buffers before
    // rendering and waiting for drawing to be complete before presenting
    VkSemaphoreCreateInfo semaphoreCreateInfo = {
    .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
    .pNext = NULL,
    .flags = ,
    }; // Create fences that we can use to throttle if we get too far
    // ahead of the image presents
    VkFenceCreateInfo fence_ci = {
    .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = NULL, .flags = VK_FENCE_CREATE_SIGNALED_BIT};
    for (uint32_t i = ; i < FRAME_LAG; i++) {
    err = vkCreateFence(demo->device, &fence_ci, NULL, &demo->fences[i]);
    assert(!err); err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, NULL, &demo->image_acquired_semaphores[i]);
    assert(!err); err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, NULL, &demo->draw_complete_semaphores[i]);
    assert(!err); if (demo->separate_present_queue) {
    err = vkCreateSemaphore(demo->device, &semaphoreCreateInfo, NULL, &demo->image_ownership_semaphores[i]);
    assert(!err);
    }
    }
    demo->frame_index = ;
  • Get Memory information and properties
      vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties);
  • demo_prepare: command buff, depth,mesh data,descriptor layout, render_pass, pipe lines
    static void demo_prepare(struct demo *demo) {
    VkResult U_ASSERT_ONLY err;
    if (demo->cmd_pool == VK_NULL_HANDLE) {
    const VkCommandPoolCreateInfo cmd_pool_info = {
    .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
    .pNext = NULL,
    .queueFamilyIndex = demo->graphics_queue_family_index,
    .flags = ,
    };
    err = vkCreateCommandPool(demo->device, &cmd_pool_info, NULL, &demo->cmd_pool);
    assert(!err);
    } const VkCommandBufferAllocateInfo cmd = {
    .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
    .pNext = NULL,
    .commandPool = demo->cmd_pool,
    .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
    .commandBufferCount = ,
    };
    err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->cmd);
    assert(!err);
    VkCommandBufferBeginInfo cmd_buf_info = {
    .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
    .pNext = NULL,
    .flags = ,
    .pInheritanceInfo = NULL,
    };
    err = vkBeginCommandBuffer(demo->cmd, &cmd_buf_info);
    assert(!err); demo_prepare_buffers(demo); if (demo->is_minimized) {
    demo->prepared = false;
    return;
    } demo_prepare_depth(demo);
    demo_prepare_textures(demo);
    demo_prepare_cube_data_buffers(demo); demo_prepare_descriptor_layout(demo);
    demo_prepare_render_pass(demo);
    demo_prepare_pipeline(demo); for (uint32_t i = ; i < demo->swapchainImageCount; i++) {
    err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->swapchain_image_resources[i].cmd);
    assert(!err);
    } if (demo->separate_present_queue) {
    const VkCommandPoolCreateInfo present_cmd_pool_info = {
    .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
    .pNext = NULL,
    .queueFamilyIndex = demo->present_queue_family_index,
    .flags = ,
    };
    err = vkCreateCommandPool(demo->device, &present_cmd_pool_info, NULL, &demo->present_cmd_pool);
    assert(!err);
    const VkCommandBufferAllocateInfo present_cmd_info = {
    .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
    .pNext = NULL,
    .commandPool = demo->present_cmd_pool,
    .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
    .commandBufferCount = ,
    };
    for (uint32_t i = ; i < demo->swapchainImageCount; i++) {
    err = vkAllocateCommandBuffers(demo->device, &present_cmd_info,
    &demo->swapchain_image_resources[i].graphics_to_present_cmd);
    assert(!err);
    demo_build_image_ownership_cmd(demo, i);
    }
    } demo_prepare_descriptor_pool(demo);
    demo_prepare_descriptor_set(demo); demo_prepare_framebuffers(demo); for (uint32_t i = ; i < demo->swapchainImageCount; i++) {
    demo->current_buffer = i;
    demo_draw_build_cmd(demo, demo->swapchain_image_resources[i].cmd);
    } /*
    * Prepare functions above may generate pipeline commands
    * that need to be flushed before beginning the render loop.
    */
    demo_flush_init_cmd(demo);
    if (demo->staging_texture.buffer) {
    demo_destroy_texture(demo, &demo->staging_texture);
    } demo->current_buffer = ;
    demo->prepared = true;
    }
  • vulkan都是提前把渲染命令准备好
    /*
    * Prepare functions above may generate pipeline commands
    * that need to be flushed before beginning the render loop.
    */
    demo_flush_init_cmd(demo);
    if (demo->staging_texture.buffer) {
    demo_destroy_texture(demo, &demo->staging_texture);
    } demo->current_buffer = ;
    demo->prepared = true;
  • 渲染主循环

    #if defined(VK_USE_PLATFORM_WIN32_KHR)
    static void demo_run(struct demo *demo) {
    if (!demo->prepared) return; demo_draw(demo);
    demo->curFrame++;
    if (demo->frameCount != INT32_MAX && demo->curFrame == demo->frameCount) {
    PostQuitMessage(validation_error);
    }
    }
    static void demo_draw(struct demo *demo) {
    VkResult U_ASSERT_ONLY err; // Ensure no more than FRAME_LAG renderings are outstanding
    vkWaitForFences(demo->device, , &demo->fences[demo->frame_index], VK_TRUE, UINT64_MAX);
    vkResetFences(demo->device, , &demo->fences[demo->frame_index]); do {
    // Get the index of the next available swapchain image:
    err =
    demo->fpAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX,
    demo->image_acquired_semaphores[demo->frame_index], VK_NULL_HANDLE, &demo->current_buffer); if (err == VK_ERROR_OUT_OF_DATE_KHR) {
    // demo->swapchain is out of date (e.g. the window was resized) and
    // must be recreated:
    demo_resize(demo);
    } else if (err == VK_SUBOPTIMAL_KHR) {
    // demo->swapchain is not as optimal as it could be, but the platform's
    // presentation engine will still present the image correctly.
    break;
    } else if (err == VK_ERROR_SURFACE_LOST_KHR) {
    vkDestroySurfaceKHR(demo->inst, demo->surface, NULL);
    demo_create_surface(demo);
    demo_resize(demo);
    } else {
    assert(!err);
    }
    } while (err != VK_SUCCESS); demo_update_data_buffer(demo); if (demo->VK_GOOGLE_display_timing_enabled) {
    // Look at what happened to previous presents, and make appropriate
    // adjustments in timing:
    DemoUpdateTargetIPD(demo); // Note: a real application would position its geometry to that it's in
    // the correct locatoin for when the next image is presented. It might
    // also wait, so that there's less latency between any input and when
    // the next image is rendered/presented. This demo program is so
    // simple that it doesn't do either of those.
    } // Wait for the image acquired semaphore to be signaled to ensure
    // that the image won't be rendered to until the presentation
    // engine has fully released ownership to the application, and it is
    // okay to render to the image.
    VkPipelineStageFlags pipe_stage_flags;
    VkSubmitInfo submit_info;
    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
    submit_info.pNext = NULL;
    submit_info.pWaitDstStageMask = &pipe_stage_flags;
    pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    submit_info.waitSemaphoreCount = ;
    submit_info.pWaitSemaphores = &demo->image_acquired_semaphores[demo->frame_index];
    submit_info.commandBufferCount = ;
    submit_info.pCommandBuffers = &demo->swapchain_image_resources[demo->current_buffer].cmd;
    submit_info.signalSemaphoreCount = ;
    submit_info.pSignalSemaphores = &demo->draw_complete_semaphores[demo->frame_index];
    err = vkQueueSubmit(demo->graphics_queue, , &submit_info, demo->fences[demo->frame_index]);
    assert(!err); if (demo->separate_present_queue) {
    // If we are using separate queues, change image ownership to the
    // present queue before presenting, waiting for the draw complete
    // semaphore and signalling the ownership released semaphore when finished
    VkFence nullFence = VK_NULL_HANDLE;
    pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
    submit_info.waitSemaphoreCount = ;
    submit_info.pWaitSemaphores = &demo->draw_complete_semaphores[demo->frame_index];
    submit_info.commandBufferCount = ;
    submit_info.pCommandBuffers = &demo->swapchain_image_resources[demo->current_buffer].graphics_to_present_cmd;
    submit_info.signalSemaphoreCount = ;
    submit_info.pSignalSemaphores = &demo->image_ownership_semaphores[demo->frame_index];
    err = vkQueueSubmit(demo->present_queue, , &submit_info, nullFence);
    assert(!err);
    } // If we are using separate queues we have to wait for image ownership,
    // otherwise wait for draw complete
    VkPresentInfoKHR present = {
    .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
    .pNext = NULL,
    .waitSemaphoreCount = ,
    .pWaitSemaphores = (demo->separate_present_queue) ? &demo->image_ownership_semaphores[demo->frame_index]
    : &demo->draw_complete_semaphores[demo->frame_index],
    .swapchainCount = ,
    .pSwapchains = &demo->swapchain,
    .pImageIndices = &demo->current_buffer,
    }; VkRectLayerKHR rect;
    VkPresentRegionKHR region;
    VkPresentRegionsKHR regions;
    if (demo->VK_KHR_incremental_present_enabled) {
    // If using VK_KHR_incremental_present, we provide a hint of the region
    // that contains changed content relative to the previously-presented
    // image. The implementation can use this hint in order to save
    // work/power (by only copying the region in the hint). The
    // implementation is free to ignore the hint though, and so we must
    // ensure that the entire image has the correctly-drawn content.
    uint32_t eighthOfWidth = demo->width / ;
    uint32_t eighthOfHeight = demo->height / ; rect.offset.x = eighthOfWidth;
    rect.offset.y = eighthOfHeight;
    rect.extent.width = eighthOfWidth * ;
    rect.extent.height = eighthOfHeight * ;
    rect.layer = ; region.rectangleCount = ;
    region.pRectangles = &rect; regions.sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR;
    regions.pNext = present.pNext;
    regions.swapchainCount = present.swapchainCount;
    regions.pRegions = &region;
    present.pNext = &regions;
    } if (demo->VK_GOOGLE_display_timing_enabled) {
    VkPresentTimeGOOGLE ptime;
    if (demo->prev_desired_present_time == ) {
    // This must be the first present for this swapchain.
    //
    // We don't know where we are relative to the presentation engine's
    // display's refresh cycle. We also don't know how long rendering
    // takes. Let's make a grossly-simplified assumption that the
    // desiredPresentTime should be half way between now and
    // now+target_IPD. We will adjust over time.
    uint64_t curtime = getTimeInNanoseconds();
    if (curtime == ) {
    // Since we didn't find out the current time, don't give a
    // desiredPresentTime:
    ptime.desiredPresentTime = ;
    } else {
    ptime.desiredPresentTime = curtime + (demo->target_IPD >> );
    }
    } else {
    ptime.desiredPresentTime = (demo->prev_desired_present_time + demo->target_IPD);
    }
    ptime.presentID = demo->next_present_id++;
    demo->prev_desired_present_time = ptime.desiredPresentTime; VkPresentTimesInfoGOOGLE present_time = {
    .sType = VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE,
    .pNext = present.pNext,
    .swapchainCount = present.swapchainCount,
    .pTimes = &ptime,
    };
    if (demo->VK_GOOGLE_display_timing_enabled) {
    present.pNext = &present_time;
    }
    } err = demo->fpQueuePresentKHR(demo->present_queue, &present);
    demo->frame_index += ;
    demo->frame_index %= FRAME_LAG; if (err == VK_ERROR_OUT_OF_DATE_KHR) {
    // demo->swapchain is out of date (e.g. the window was resized) and
    // must be recreated:
    demo_resize(demo);
    } else if (err == VK_SUBOPTIMAL_KHR) {
    // demo->swapchain is not as optimal as it could be, but the platform's
    // presentation engine will still present the image correctly.
    } else if (err == VK_ERROR_SURFACE_LOST_KHR) {
    vkDestroySurfaceKHR(demo->inst, demo->surface, NULL);
    demo_create_surface(demo);
    demo_resize(demo);
    } else {
    assert(!err);
    }
    }

最终图形画出来的效果如下图所示。

总结

这里我只是简单的了解了Vulkan渲染的流程,但是其中的概念不是很清晰。按照张静的观点,Vulkan 很酷很炫,但是不适合你,早点分手吧 ,似乎

学习vulkan是一件得不偿失的事情,我自己思考了下,我为什么要学这个呢?

1) 我对图形API感兴趣,我想知道他是怎么和硬件结合的,Vulkan就是图形API内的汇编,吃透了他,估计其他的几种API都不是问题,手动狗头;

2) 工作需要,如果我要想采用商用引擎来接入合作项目,那必须对底层的硬件调用有所了解,这样才能做深入的定制集成;

3) 性能优化是我给自己的定位,如果我连这些都不清楚,如何优化。

所以不要再犹豫了,学哪个都不容易,坚持下去吧,一年之后再看。

Reference:

1、知乎Vulkan-高性能渲染

2、Life of a triangle - NVIDIA's logical pipeline

3、Round Robin 算法

4、NVIDIA Developer Vulkan

5、Vulkan SDK Tutorial

6、Vulkan In 30 Minutes

7、Vulkan Notes

8、GDC  2016 Talk

9、知乎: Vulkan编程指南

10、Shader交叉编译之梦

11、游戏引擎随笔: 现代图形API

12、SPIR-V

13、Khronos Vulkan Registry : vulkan specifics

14、vulkan踩坑记

15、Vulkan C++ examples and demos

16、NVIDIA GameWorks Graphics Samples

最新文章

  1. ulipad源码包配置环境及安装
  2. 使用springMVC实现文件上传和下载之文件下载
  3. jquer ajax
  4. vmware 8下ubuntu 13.04安装vmware tools
  5. powerdesigner 转换各种数据库SQL
  6. C-最长回文子串(2)
  7. 基于visual Studio2013解决C语言竞赛题之1070删除相同节点
  8. SRM 627 D1L2GraphInversionsDFS查找指定长度的所有路径 Binary indexed tree (BIT)
  9. 数据库-增删改查操作SQL实现
  10. 初读&quot;Thinking in Java&quot;读书笔记之第二章 --- 一切都是对象
  11. java对象深复制、浅复制(深拷贝、浅拷贝)的理解
  12. Android版数据结构与算法(五):LinkedHashMap核心源码彻底分析
  13. iOS调用系统发送短信和邮件分享
  14. 使用代码检查Dynamics 365中的备用键状态
  15. 如何将plist大图拆分成原来的小图
  16. day 7-9 IO模型
  17. VUE页面刷新问题
  18. Android EventBus3.x 使用详解
  19. 大型运输行业实战_day01_1_业务分析
  20. php面向对象高级-魔术方法与迭代器

热门文章

  1. Struts配置文件报错&quot;元素类型为 &quot;package&quot; 的内容必须匹配&quot;
  2. centos 7中添加一个新用户并授权的步骤详解
  3. Nmap 使用
  4. 关于html的基本知识
  5. 37 java序列化与反序列化
  6. python对ASC码的加减
  7. A. Optimal Currency Exchange 兑换硬币,剩下的钱最少
  8. maven构建项目失败----99%
  9. coding 321
  10. 【高软作业4】:Tomcat 观察者模式解析 之 Lifecycle