Vulkan SDK Demo 之一 熟悉

佐手、 提交于 2020-02-03 00:56:53

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 = 0;
            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 = 0,
            .pEngineName = APP_SHORT_NAME,
            .engineVersion = 0,
            .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 = 0;
            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 > 0) {
            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[0];
            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 >= 1);
    
        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 = 0;
        VkBool32 swapchainExtFound = 0;
        demo->enabled_extension_count = 0;
        memset(demo->extension_names, 0, sizeof(demo->extension_names));
    
        err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL, &device_extension_count, NULL);
        assert(!err);
    
        if (device_extension_count > 0) {
            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 = 0; i < device_extension_count; i++) {
                if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, device_extensions[i].extensionName)) {
                    swapchainExtFound = 1;
                    demo->extension_names[demo->enabled_extension_count++] = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
                }
                assert(demo->enabled_extension_count < 64);
            }
    
            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 = 0; 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 < 64);
                }
                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 = 0; 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 < 64);
                }
                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 = 0;
    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[1] = {0.0};
        VkDeviceQueueCreateInfo queues[2];
        queues[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
        queues[0].pNext = NULL;
        queues[0].queueFamilyIndex = demo->graphics_queue_family_index;
        queues[0].queueCount = 1;
        queues[0].pQueuePriorities = queue_priorities;
        queues[0].flags = 0;
    
        VkDeviceCreateInfo device = {
            .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
            .pNext = NULL,
            .queueCreateInfoCount = 1,
            .pQueueCreateInfos = queues,
            .enabledLayerCount = 0,
            .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[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
            queues[1].pNext = NULL;
            queues[1].queueFamilyIndex = demo->present_queue_family_index;
            queues[1].queueCount = 1;
            queues[1].pQueuePriorities = queue_priorities;
            queues[1].flags = 0;
            device.queueCreateInfoCount = 2;
        }
        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 == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) {
            demo->format = VK_FORMAT_B8G8R8A8_UNORM;
        } else {
            assert(formatCount >= 1);
            demo->format = surfFormats[0].format;
        }
        demo->color_space = surfFormats[0].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 = 0,
        }; 
    
        // 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 = 0; 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 = 0;
  • 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 = 0,
            };
            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 = 1,
        };
        err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->cmd);
        assert(!err);
        VkCommandBufferBeginInfo cmd_buf_info = {
            .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
            .pNext = NULL,
            .flags = 0,
            .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 = 0; 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 = 0,
            };
            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 = 1,
            };
            for (uint32_t i = 0; 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 = 0; 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 = 0;
        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 = 0;
        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, 1, &demo->fences[demo->frame_index], VK_TRUE, UINT64_MAX);
        vkResetFences(demo->device, 1, &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 = 1;
        submit_info.pWaitSemaphores = &demo->image_acquired_semaphores[demo->frame_index];
        submit_info.commandBufferCount = 1;
        submit_info.pCommandBuffers = &demo->swapchain_image_resources[demo->current_buffer].cmd;
        submit_info.signalSemaphoreCount = 1;
        submit_info.pSignalSemaphores = &demo->draw_complete_semaphores[demo->frame_index];
        err = vkQueueSubmit(demo->graphics_queue, 1, &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 = 1;
            submit_info.pWaitSemaphores = &demo->draw_complete_semaphores[demo->frame_index];
            submit_info.commandBufferCount = 1;
            submit_info.pCommandBuffers = &demo->swapchain_image_resources[demo->current_buffer].graphics_to_present_cmd;
            submit_info.signalSemaphoreCount = 1;
            submit_info.pSignalSemaphores = &demo->image_ownership_semaphores[demo->frame_index];
            err = vkQueueSubmit(demo->present_queue, 1, &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 = 1,
            .pWaitSemaphores = (demo->separate_present_queue) ? &demo->image_ownership_semaphores[demo->frame_index]
                                                              : &demo->draw_complete_semaphores[demo->frame_index],
            .swapchainCount = 1,
            .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 / 8;
            uint32_t eighthOfHeight = demo->height / 8;
    
            rect.offset.x = eighthOfWidth;
            rect.offset.y = eighthOfHeight;
            rect.extent.width = eighthOfWidth * 6;
            rect.extent.height = eighthOfHeight * 6;
            rect.layer = 0;
    
            region.rectangleCount = 1;
            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 == 0) {
                // 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 == 0) {
                    // Since we didn't find out the current time, don't give a
                    // desiredPresentTime:
                    ptime.desiredPresentTime = 0;
                } else {
                    ptime.desiredPresentTime = curtime + (demo->target_IPD >> 1);
                }
            } 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 += 1;
        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

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!