blob: 7ae845b417f89c80a0501e224e808147f8e93c5c [file] [log] [blame]
// 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