Vulkan SDK Demo 之一 熟悉
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);
-
/* 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 = ▭ regions.sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR; regions.pNext = present.pNext; regions.swapchainCount = present.swapchainCount; regions.pRegions = ®ion; present.pNext = ®ions; } 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
9、知乎: Vulkan编程指南
10、Shader交叉编译之梦
12、SPIR-V
13、Khronos Vulkan Registry : vulkan specifics
14、vulkan踩坑记
15、Vulkan C++ examples and demos
16、NVIDIA GameWorks Graphics Samples