| // 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. |
| |
| #ifndef GPU_VULKAN_VULKAN_SWAP_CHAIN_H_ |
| #define GPU_VULKAN_VULKAN_SWAP_CHAIN_H_ |
| |
| #include <vulkan/vulkan_core.h> |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "base/callback.h" |
| #include "base/component_export.h" |
| #include "base/containers/circular_deque.h" |
| #include "base/memory/raw_ptr.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/synchronization/condition_variable.h" |
| #include "base/synchronization/lock.h" |
| #include "base/threading/thread_checker.h" |
| #include "third_party/abseil-cpp/absl/types/optional.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 { |
| public: |
| class COMPONENT_EXPORT(VULKAN) ScopedWrite { |
| public: |
| explicit ScopedWrite(VulkanSwapChain* swap_chain); |
| ScopedWrite(ScopedWrite&& other); |
| ~ScopedWrite(); |
| |
| ScopedWrite(const ScopedWrite&) = delete; |
| ScopedWrite& operator=(const ScopedWrite&) = delete; |
| |
| const ScopedWrite& operator=(ScopedWrite&& other); |
| |
| void Reset(); |
| |
| 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_; } |
| |
| private: |
| VulkanSwapChain* swap_chain_ = nullptr; |
| 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); |
| |
| VulkanSwapChain(const VulkanSwapChain&) = delete; |
| VulkanSwapChain& operator=(const VulkanSwapChain&) = delete; |
| |
| ~VulkanSwapChain(); |
| |
| // 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, |
| 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 = |
| base::OnceCallback<void(gfx::SwapResult)>; |
| 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_; |
| } |
| |
| uint32_t current_image_index() const { |
| base::AutoLock auto_lock(lock_); |
| DCHECK(acquired_image_); |
| return *acquired_image_; |
| } |
| |
| VkResult state() const { |
| base::AutoLock auto_lock(lock_); |
| return state_; |
| } |
| |
| private: |
| 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, |
| std::unique_ptr<VulkanSwapChain> old_swap_chain) |
| EXCLUSIVE_LOCKS_REQUIRED(lock_); |
| void DestroySwapChain() EXCLUSIVE_LOCKS_REQUIRED(lock_); |
| |
| bool InitializeSwapImages(const VkSurfaceFormatKHR& surface_format) |
| EXCLUSIVE_LOCKS_REQUIRED(lock_); |
| 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_); |
| |
| bool GetOrCreateSemaphores(VkSemaphore* acquire_semaphore, |
| VkSemaphore* present_semaphore) |
| EXCLUSIVE_LOCKS_REQUIRED(lock_); |
| void ReturnSemaphores(VkSemaphore acquire_semaphore, |
| VkSemaphore present_semaphore) |
| EXCLUSIVE_LOCKS_REQUIRED(lock_); |
| |
| mutable base::Lock lock_; |
| |
| const uint64_t acquire_next_image_timeout_ns_; |
| |
| raw_ptr<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. |
| absl::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_; |
| |
| // Available semaphores can be reused when waiting semaphores is over. |
| struct PendingSemaphores { |
| VkSemaphore acquire_semaphore = VK_NULL_HANDLE; |
| VkSemaphore present_semaphore = VK_NULL_HANDLE; |
| }; |
| base::circular_deque<PendingSemaphores> pending_semaphores_queue_ |
| GUARDED_BY(lock_); |
| |
| THREAD_CHECKER(thread_checker_); |
| }; |
| |
| } // namespace gpu |
| |
| #endif // GPU_VULKAN_VULKAN_SWAP_CHAIN_H_ |