| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "vulkan_window.h" |
| |
| namespace vulkan { |
| |
| VulkanWindow::VulkanWindow(std::unique_ptr<VulkanSurface> platform_surface) |
| : valid_(false), platform_surface_(std::move(platform_surface)) { |
| if (platform_surface_ == nullptr || !platform_surface_->IsValid()) { |
| return; |
| } |
| |
| if (!vk.IsValid()) { |
| return; |
| } |
| |
| if (!CreateInstance()) { |
| return; |
| } |
| |
| if (!CreateSurface()) { |
| return; |
| } |
| |
| if (!SelectPhysicalDevice()) { |
| return; |
| } |
| |
| if (!CreateLogicalDevice()) { |
| return; |
| } |
| |
| if (!CreateSwapChain()) { |
| return; |
| } |
| |
| if (!AcquireDeviceQueue()) { |
| return; |
| } |
| |
| if (!CreateCommandPool()) { |
| return; |
| } |
| |
| if (!SetupBuffers()) { |
| return; |
| } |
| |
| valid_ = true; |
| } |
| |
| VulkanWindow::~VulkanWindow() = default; |
| |
| bool VulkanWindow::IsValid() const { |
| return valid_; |
| } |
| |
| bool VulkanWindow::CreateInstance() { |
| const VkApplicationInfo info = { |
| .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, |
| .pNext = nullptr, |
| .pApplicationName = "FlutterEngine", |
| .applicationVersion = VK_MAKE_VERSION(1, 0, 0), |
| .pEngineName = "FlutterEngine", |
| .engineVersion = VK_MAKE_VERSION(1, 0, 0), |
| .apiVersion = VK_MAKE_VERSION(1, 0, 0), |
| }; |
| |
| const char* extensions[] = { |
| VK_KHR_SURFACE_EXTENSION_NAME, platform_surface_->ExtensionName(), |
| }; |
| |
| const VkInstanceCreateInfo create_info = { |
| .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .pApplicationInfo = &info, |
| .enabledLayerCount = 0, |
| .ppEnabledLayerNames = nullptr, |
| .enabledExtensionCount = sizeof(extensions) / sizeof(const char*), |
| .ppEnabledExtensionNames = extensions, |
| }; |
| |
| VkInstance instance = VK_NULL_HANDLE; |
| if (vk.createInstance(&create_info, nullptr, &instance) != VK_SUCCESS) { |
| return false; |
| } |
| |
| instance_ = {instance, |
| [this](VkInstance i) { vk.destroyInstance(i, nullptr); }}; |
| |
| return true; |
| } |
| |
| bool VulkanWindow::CreateSurface() { |
| if (!instance_) { |
| return false; |
| } |
| |
| VkSurfaceKHR surface = platform_surface_->CreateSurfaceHandle(vk, instance_); |
| |
| if (surface == VK_NULL_HANDLE) { |
| return false; |
| } |
| |
| surface_ = {surface, [this](VkSurfaceKHR surface) { |
| vk.destroySurfaceKHR(instance_, surface, nullptr); |
| }}; |
| |
| return true; |
| } |
| |
| bool VulkanWindow::SelectPhysicalDevice() { |
| if (instance_ == nullptr) { |
| return false; |
| } |
| |
| uint32_t device_count = 0; |
| if (vk.enumeratePhysicalDevices(instance_, &device_count, nullptr) != |
| VK_SUCCESS) { |
| return false; |
| } |
| |
| if (device_count == 0) { |
| // No available devices. |
| return false; |
| } |
| |
| VkPhysicalDevice devices[device_count]; |
| |
| if (vk.enumeratePhysicalDevices(instance_, &device_count, devices) != |
| VK_SUCCESS) { |
| return false; |
| } |
| |
| // Pick the first one available. |
| physical_device_ = {devices[0], {}}; |
| return true; |
| } |
| |
| bool VulkanWindow::CreateLogicalDevice() { |
| if (physical_device_ == nullptr) { |
| return false; |
| } |
| |
| float priorities[] = {1.0f}; |
| |
| const VkDeviceQueueCreateInfo queue_create = { |
| .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .queueFamilyIndex = 0, |
| .queueCount = 1, |
| .pQueuePriorities = priorities, |
| }; |
| |
| const char* extensions[] = { |
| VK_KHR_SWAPCHAIN_EXTENSION_NAME, |
| }; |
| |
| const VkDeviceCreateInfo create_info = { |
| .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = 0, |
| .queueCreateInfoCount = 1, |
| .pQueueCreateInfos = &queue_create, |
| .enabledLayerCount = 0, |
| .ppEnabledLayerNames = nullptr, |
| .enabledExtensionCount = sizeof(extensions) / sizeof(const char*), |
| .ppEnabledExtensionNames = extensions, |
| .pEnabledFeatures = nullptr, |
| }; |
| |
| VkDevice device = VK_NULL_HANDLE; |
| |
| if (vk.createDevice(physical_device_, &create_info, nullptr, &device) != |
| VK_SUCCESS) { |
| return false; |
| } |
| |
| device_ = {device, |
| [this](VkDevice device) { vk.destroyDevice(device, nullptr); }}; |
| |
| return true; |
| } |
| |
| bool VulkanWindow::AcquireDeviceQueue() { |
| if (device_ == nullptr) { |
| return false; |
| } |
| |
| VkQueue queue = VK_NULL_HANDLE; |
| vk.getDeviceQueue(device_, 0, 0, &queue); |
| |
| if (queue == VK_NULL_HANDLE) { |
| return false; |
| } |
| |
| queue_ = {queue, {}}; |
| |
| return true; |
| } |
| |
| std::pair<bool, VkSurfaceFormatKHR> VulkanWindow::ChooseSurfaceFormat() { |
| if (!physical_device_ || !surface_) { |
| return {false, {}}; |
| } |
| |
| uint32_t format_count = 0; |
| if (vk.getPhysicalDeviceSurfaceFormatsKHR( |
| physical_device_, surface_, &format_count, nullptr) != VK_SUCCESS) { |
| return {false, {}}; |
| } |
| |
| if (format_count == 0) { |
| return {false, {}}; |
| } |
| |
| VkSurfaceFormatKHR formats[format_count]; |
| if (vk.getPhysicalDeviceSurfaceFormatsKHR( |
| physical_device_, surface_, &format_count, formats) != VK_SUCCESS) { |
| return {false, {}}; |
| } |
| |
| for (uint32_t i = 0; i < format_count; i++) { |
| if (formats[i].format == VK_FORMAT_R8G8B8A8_UNORM) { |
| return {true, formats[i]}; |
| } |
| } |
| |
| return {false, {}}; |
| } |
| |
| std::pair<bool, VkPresentModeKHR> VulkanWindow::ChoosePresentMode() { |
| if (!physical_device_ || !surface_) { |
| return {false, {}}; |
| } |
| |
| uint32_t modes_count = 0; |
| |
| if (vk.getPhysicalDeviceSurfacePresentModesKHR( |
| physical_device_, surface_, &modes_count, nullptr) != VK_SUCCESS) { |
| return {false, {}}; |
| } |
| |
| if (modes_count == 0) { |
| return {false, {}}; |
| } |
| |
| VkPresentModeKHR modes[modes_count]; |
| |
| if (vk.getPhysicalDeviceSurfacePresentModesKHR( |
| physical_device_, surface_, &modes_count, modes) != VK_SUCCESS) { |
| return {false, {}}; |
| } |
| |
| for (uint32_t i = 0; i < modes_count; i++) { |
| if (modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) { |
| return {true, modes[i]}; |
| } |
| } |
| |
| return {true, VK_PRESENT_MODE_FIFO_KHR}; |
| } |
| |
| bool VulkanWindow::CreateSwapChain() { |
| if (!device_ || !surface_) { |
| return false; |
| } |
| |
| // Query Capabilities |
| |
| VkSurfaceCapabilitiesKHR capabilities = {0}; |
| if (vk.getPhysicalDeviceSurfaceCapabilitiesKHR(physical_device_, surface_, |
| &capabilities) != VK_SUCCESS) { |
| return false; |
| } |
| |
| // Query Format |
| |
| VkSurfaceFormatKHR format = {}; |
| bool query_result = false; |
| |
| std::tie(query_result, format) = ChooseSurfaceFormat(); |
| |
| if (!query_result) { |
| return false; |
| } |
| |
| // Query Present Mode |
| |
| VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR; |
| std::tie(query_result, presentMode) = ChoosePresentMode(); |
| |
| if (!query_result) { |
| return false; |
| } |
| |
| // Construct the Swapchain |
| |
| const VkSwapchainCreateInfoKHR create_info = { |
| .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, |
| .pNext = nullptr, |
| .flags = 0, |
| .surface = surface_, |
| .minImageCount = capabilities.minImageCount, |
| .imageFormat = format.format, |
| .imageColorSpace = format.colorSpace, |
| .imageExtent = capabilities.currentExtent, |
| .imageArrayLayers = 1, |
| .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
| .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, |
| .queueFamilyIndexCount = 0, |
| .pQueueFamilyIndices = nullptr, |
| .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR, |
| .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, |
| .presentMode = presentMode, |
| .clipped = VK_FALSE, |
| .oldSwapchain = VK_NULL_HANDLE, |
| }; |
| |
| VkSwapchainKHR swapchain = VK_NULL_HANDLE; |
| |
| if (vk.createSwapchainKHR(device_, &create_info, nullptr, &swapchain) != |
| VK_SUCCESS) { |
| return false; |
| } |
| |
| swapchain_ = {swapchain, [this](VkSwapchainKHR swapchain) { |
| vk.destroySwapchainKHR(device_, swapchain, nullptr); |
| }}; |
| |
| return true; |
| } |
| |
| bool VulkanWindow::SetupBuffers() { |
| if (!device_ || !swapchain_) { |
| return false; |
| } |
| |
| uint32_t count = 0; |
| if (vk.getSwapchainImagesKHR(device_, swapchain_, &count, nullptr) != |
| VK_SUCCESS) { |
| return false; |
| } |
| |
| if (count == 0) { |
| return false; |
| } |
| |
| VkImage images[count]; |
| |
| if (vk.getSwapchainImagesKHR(device_, swapchain_, &count, images) != |
| VK_SUCCESS) { |
| return false; |
| } |
| |
| if (count == 0) { |
| return false; |
| } |
| |
| auto image_collector = [](VkImage image) { |
| // We are only just getting references to images owned by the swapchain. |
| // There is no ownership change. |
| }; |
| |
| backbuffers_.clear(); |
| |
| for (uint32_t i = 0; i < count; i++) { |
| std::unique_ptr<VulkanBackbuffer> backbuffer(new VulkanBackbuffer( |
| vk, device_, command_pool_, {images[i], image_collector})); |
| |
| if (!backbuffer->IsValid()) { |
| return false; |
| } |
| |
| backbuffers_.emplace_back(std::move(backbuffer)); |
| } |
| |
| return true; |
| } |
| |
| bool VulkanWindow::CreateCommandPool() { |
| if (!device_) { |
| return false; |
| } |
| |
| const VkCommandPoolCreateInfo create_info = { |
| .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, |
| .pNext = nullptr, |
| .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, |
| .queueFamilyIndex = 0, |
| }; |
| |
| VkCommandPool command_pool = VK_NULL_HANDLE; |
| if (vk.createCommandPool(device_, &create_info, nullptr, &command_pool) != |
| VK_SUCCESS) { |
| return false; |
| } |
| |
| command_pool_ = {command_pool, [this](VkCommandPool pool) { |
| vk.destroyCommandPool(device_, pool, nullptr); |
| }}; |
| return true; |
| } |
| |
| } // namespace vulkan |