blob: 160cb09de8c307a7bcda61d59ad425ce288d0b6f [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 <vulkan/vulkan.h>
#include <memory>
#include <vector>
#include "base/callback.h"
#include "base/component_export.h"
#include "base/containers/circular_deque.h"
#include "base/memory/scoped_refptr.h"
#include "base/optional.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "build/build_config.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/swap_result.h"
namespace gpu {
class VulkanDeviceQueue;
class COMPONENT_EXPORT(VULKAN) VulkanSwapChain {
explicit ScopedWrite(VulkanSwapChain* swap_chain);
bool success() const { return success_; }
VkImage image() const { return image_; }
uint32_t image_index() const { return image_index_; }
VkImageLayout image_layout() const { return image_layout_; }
VkImageUsageFlags image_usage() const { return image_usage_; }
VkSemaphore begin_semaphore() const { return begin_semaphore_; }
VkSemaphore end_semaphore() const { return end_semaphore_; }
VulkanSwapChain* const swap_chain_;
bool success_ = false;
VkImage image_ = VK_NULL_HANDLE;
uint32_t image_index_ = 0;
VkImageLayout image_layout_ = VK_IMAGE_LAYOUT_UNDEFINED;
VkImageUsageFlags image_usage_ = 0;
VkSemaphore begin_semaphore_ = VK_NULL_HANDLE;
VkSemaphore end_semaphore_ = VK_NULL_HANDLE;
explicit VulkanSwapChain(uint64_t acquire_next_image_timeout_ns);
// min_image_count is the minimum number of presentable images.
bool Initialize(VulkanDeviceQueue* device_queue,
VkSurfaceKHR surface,
const VkSurfaceFormatKHR& surface_format,
const gfx::Size& image_size,
uint32_t min_image_count,
VkImageUsageFlags image_usage_flags,
VkSurfaceTransformFlagBitsKHR pre_transform,
bool use_protected_memory,
std::unique_ptr<VulkanSwapChain> old_swap_chain);
// Destroy() should be called when all related GPU tasks have been finished.
void Destroy();
// Present the current buffer.
gfx::SwapResult PostSubBuffer(const gfx::Rect& rect);
using PostSubBufferCompletionCallback =
void PostSubBufferAsync(const gfx::Rect& rect,
PostSubBufferCompletionCallback callback);
uint32_t num_images() const {
// size of |images_| will not be changed after initializing, so it is safe
// to read it here.
return static_cast<uint32_t>(TS_UNCHECKED_READ(images_).size());
const gfx::Size& size() const {
// |size_| is never changed after initialization.
return size_;
bool use_protected_memory() const {
// |use_protected_memory_| is never changed after initialization.
return use_protected_memory_;
uint32_t current_image_index() const {
base::AutoLock auto_lock(lock_);
return *acquired_image_;
VkResult state() const {
base::AutoLock auto_lock(lock_);
return state_;
struct ImageData {
VkImage image = VK_NULL_HANDLE;
VkImageLayout image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
// Semaphore used for vkAcquireNextImageKHR()
VkSemaphore acquire_semaphore = VK_NULL_HANDLE;
// Semaphore used for vkQueuePresentKHR()
VkSemaphore present_semaphore = VK_NULL_HANDLE;
bool InitializeSwapChain(VkSurfaceKHR surface,
const VkSurfaceFormatKHR& surface_format,
const gfx::Size& image_size,
uint32_t min_image_count,
VkImageUsageFlags image_usage_flags,
VkSurfaceTransformFlagBitsKHR pre_transform,
bool use_protected_memory,
std::unique_ptr<VulkanSwapChain> old_swap_chain)
void DestroySwapChain() EXCLUSIVE_LOCKS_REQUIRED(lock_);
bool InitializeSwapImages(const VkSurfaceFormatKHR& surface_format)
void DestroySwapImages() EXCLUSIVE_LOCKS_REQUIRED(lock_);
bool BeginWriteCurrentImage(VkImage* image,
uint32_t* image_index,
VkImageLayout* layout,
VkImageUsageFlags* usage,
VkSemaphore* begin_semaphore,
VkSemaphore* end_semaphore);
void EndWriteCurrentImage();
bool PresentBuffer(const gfx::Rect& rect) EXCLUSIVE_LOCKS_REQUIRED(lock_);
bool AcquireNextImage() EXCLUSIVE_LOCKS_REQUIRED(lock_);
// Wait until PostSubBufferAsync() is finished on ThreadPool.
void WaitUntilPostSubBufferAsyncFinished() EXCLUSIVE_LOCKS_REQUIRED(lock_);
struct FenceAndSemaphores {
VkFence fence = VK_NULL_HANDLE;
VkSemaphore semaphores[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
FenceAndSemaphores GetOrCreateFenceAndSemaphores()
void ReturnFenceAndSemaphores(const FenceAndSemaphores& fence_and_semaphores)
mutable base::Lock lock_;
const uint64_t acquire_next_image_timeout_ns_;
bool use_protected_memory_ = false;
VulkanDeviceQueue* device_queue_ = nullptr;
bool is_incremental_present_supported_ = false;
VkSwapchainKHR swap_chain_ GUARDED_BY(lock_) = VK_NULL_HANDLE;
gfx::Size size_;
// Images in the swap chain.
std::vector<ImageData> images_ GUARDED_BY(lock_);
VkImageUsageFlags image_usage_ = 0;
// True if BeginWriteCurrentImage() is called, but EndWriteCurrentImage() is
// not.
bool is_writing_ GUARDED_BY(lock_) = false;
// True if the current image is new required, and BeginWriteCurrentImage() and
// EndWriteCurrentImage() pair are not called.
bool new_acquired_ GUARDED_BY(lock_) = true;
// Condition variable is signalled when a PostSubBufferAsync() is finished.
base::ConditionVariable condition_variable_{&lock_};
// True if there is pending post sub buffer in the fly.
bool has_pending_post_sub_buffer_ GUARDED_BY(lock_) = false;
// The current swapchain state_.
VkResult state_ GUARDED_BY(lock_) = VK_SUCCESS;
// Acquired images queue.
base::Optional<uint32_t> acquired_image_ GUARDED_BY(lock_);
bool destroy_swapchain_will_hang_ = false;
// For executing PosSubBufferAsync tasks off the GPU main thread.
scoped_refptr<base::SequencedTaskRunner> post_sub_buffer_task_runner_;
#if !defined(OS_FUCHSIA)
// Available fence and semaphores can be reused when fence is passed.
// Not used on Fuchsia because Fuchsia's swapchain implementation doesn't
// support fences.
base::circular_deque<FenceAndSemaphores> fence_and_semaphores_queue_
} // namespace gpu