| // Copyright (c) 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 "gpu/vulkan/vulkan_surface.h" |
| |
| #include <vulkan/vulkan.h> |
| |
| #include "base/macros.h" |
| #include "base/stl_util.h" |
| #include "gpu/vulkan/vulkan_device_queue.h" |
| #include "gpu/vulkan/vulkan_function_pointers.h" |
| #include "gpu/vulkan/vulkan_swap_chain.h" |
| |
| namespace gpu { |
| |
| namespace { |
| const VkFormat kPreferredVkFormats32[] = { |
| VK_FORMAT_B8G8R8A8_UNORM, // FORMAT_BGRA8888, |
| VK_FORMAT_R8G8B8A8_UNORM, // FORMAT_RGBA8888, |
| }; |
| |
| const VkFormat kPreferredVkFormats16[] = { |
| VK_FORMAT_R5G6B5_UNORM_PACK16, // FORMAT_RGB565, |
| }; |
| |
| } // namespace |
| |
| VulkanSurface::~VulkanSurface() { |
| DCHECK_EQ(static_cast<VkSurfaceKHR>(VK_NULL_HANDLE), surface_); |
| } |
| |
| VulkanSurface::VulkanSurface(VkInstance vk_instance, VkSurfaceKHR surface) |
| : vk_instance_(vk_instance), surface_(surface) { |
| DCHECK_NE(static_cast<VkSurfaceKHR>(VK_NULL_HANDLE), surface_); |
| } |
| |
| bool VulkanSurface::Initialize(VulkanDeviceQueue* device_queue, |
| VulkanSurface::Format format) { |
| DCHECK(format >= 0 && format < NUM_SURFACE_FORMATS); |
| DCHECK(device_queue); |
| |
| device_queue_ = device_queue; |
| |
| VkResult result = VK_SUCCESS; |
| |
| VkBool32 present_support; |
| if (vkGetPhysicalDeviceSurfaceSupportKHR( |
| device_queue_->GetVulkanPhysicalDevice(), |
| device_queue_->GetVulkanQueueIndex(), surface_, |
| &present_support) != VK_SUCCESS) { |
| DLOG(ERROR) << "vkGetPhysicalDeviceSurfaceSupportKHR() failed: " << result; |
| return false; |
| } |
| if (!present_support) { |
| DLOG(ERROR) << "Surface not supported by present queue."; |
| return false; |
| } |
| |
| // Get list of supported formats. |
| uint32_t format_count = 0; |
| result = vkGetPhysicalDeviceSurfaceFormatsKHR( |
| device_queue_->GetVulkanPhysicalDevice(), surface_, &format_count, |
| nullptr); |
| if (VK_SUCCESS != result) { |
| DLOG(ERROR) << "vkGetPhysicalDeviceSurfaceFormatsKHR() failed: " << result; |
| return false; |
| } |
| |
| std::vector<VkSurfaceFormatKHR> formats(format_count); |
| result = vkGetPhysicalDeviceSurfaceFormatsKHR( |
| device_queue_->GetVulkanPhysicalDevice(), surface_, &format_count, |
| formats.data()); |
| if (VK_SUCCESS != result) { |
| DLOG(ERROR) << "vkGetPhysicalDeviceSurfaceFormatsKHR() failed: " << result; |
| return false; |
| } |
| |
| const VkFormat* preferred_formats = (format == FORMAT_RGBA_32) |
| ? kPreferredVkFormats32 |
| : kPreferredVkFormats16; |
| unsigned int size = (format == FORMAT_RGBA_32) |
| ? base::size(kPreferredVkFormats32) |
| : base::size(kPreferredVkFormats16); |
| |
| if (formats.size() == 1 && VK_FORMAT_UNDEFINED == formats[0].format) { |
| surface_format_.format = preferred_formats[0]; |
| surface_format_.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; |
| } else { |
| bool format_set = false; |
| for (VkSurfaceFormatKHR supported_format : formats) { |
| unsigned int counter = 0; |
| while (counter < size && format_set == false) { |
| if (supported_format.format == preferred_formats[counter]) { |
| surface_format_ = supported_format; |
| surface_format_.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; |
| format_set = true; |
| } |
| counter++; |
| } |
| if (format_set) |
| break; |
| } |
| if (!format_set) { |
| DLOG(ERROR) << "Format not supported."; |
| return false; |
| } |
| } |
| // Delay creating SwapChain to when the surface size is specified by Resize(). |
| return true; |
| } |
| |
| void VulkanSurface::Destroy() { |
| swap_chain_->Destroy(); |
| vkDestroySurfaceKHR(vk_instance_, surface_, nullptr); |
| surface_ = VK_NULL_HANDLE; |
| } |
| |
| gfx::SwapResult VulkanSurface::SwapBuffers() { |
| return swap_chain_->SwapBuffers(); |
| } |
| |
| VulkanSwapChain* VulkanSurface::GetSwapChain() { |
| return swap_chain_.get(); |
| } |
| |
| void VulkanSurface::Finish() { |
| vkQueueWaitIdle(device_queue_->GetVulkanQueue()); |
| } |
| |
| bool VulkanSurface::SetSize(const gfx::Size& size) { |
| // Get Surface Information. |
| VkSurfaceCapabilitiesKHR surface_caps; |
| VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR( |
| device_queue_->GetVulkanPhysicalDevice(), surface_, &surface_caps); |
| if (VK_SUCCESS != result) { |
| DLOG(ERROR) << "vkGetPhysicalDeviceSurfaceCapabilitiesKHR() failed: " |
| << result; |
| return false; |
| } |
| |
| // If width and height of the surface are 0xFFFFFFFF, it means the surface |
| // size will be determined by the extent of a swapchain targeting the surface. |
| // In that case, we will use the |size| which is the window size for the |
| // swapchain. Otherwise, we just use the current surface size for the |
| // swapchian. |
| if (surface_caps.currentExtent.width == |
| std::numeric_limits<uint32_t>::max() || |
| surface_caps.currentExtent.height == |
| std::numeric_limits<uint32_t>::max()) { |
| DCHECK_EQ(surface_caps.currentExtent.width, |
| std::numeric_limits<uint32_t>::max()); |
| DCHECK_EQ(surface_caps.currentExtent.height, |
| std::numeric_limits<uint32_t>::max()); |
| surface_caps.currentExtent.width = size.width(); |
| surface_caps.currentExtent.height = size.height(); |
| } |
| |
| DCHECK_GE(surface_caps.currentExtent.width, |
| surface_caps.minImageExtent.width); |
| DCHECK_GE(surface_caps.currentExtent.height, |
| surface_caps.minImageExtent.height); |
| DCHECK_LE(surface_caps.currentExtent.width, |
| surface_caps.maxImageExtent.width); |
| DCHECK_LE(surface_caps.currentExtent.height, |
| surface_caps.maxImageExtent.height); |
| DCHECK_GT(surface_caps.currentExtent.width, 0u); |
| DCHECK_GT(surface_caps.currentExtent.height, 0u); |
| |
| gfx::Size new_size(surface_caps.currentExtent.width, |
| surface_caps.currentExtent.height); |
| if (size_ == new_size) |
| return true; |
| |
| size_ = new_size; |
| auto swap_chain = std::make_unique<VulkanSwapChain>(); |
| // Create Swapchain. |
| if (!swap_chain->Initialize(device_queue_, surface_, surface_caps, |
| surface_format_, std::move(swap_chain_))) { |
| return false; |
| } |
| |
| swap_chain_ = std::move(swap_chain); |
| return true; |
| } |
| |
| } // namespace gpu |