1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/viz/common/gpu/vulkan_in_process_context_provider.h"
6 
7 #include <utility>
8 
9 #include "gpu/vulkan/buildflags.h"
10 #include "gpu/vulkan/init/gr_vk_memory_allocator_impl.h"
11 #include "gpu/vulkan/vulkan_device_queue.h"
12 #include "gpu/vulkan/vulkan_fence_helper.h"
13 #include "gpu/vulkan/vulkan_function_pointers.h"
14 #include "gpu/vulkan/vulkan_implementation.h"
15 #include "gpu/vulkan/vulkan_instance.h"
16 #include "gpu/vulkan/vulkan_util.h"
17 #include "third_party/skia/include/gpu/GrDirectContext.h"
18 #include "third_party/skia/include/gpu/vk/GrVkExtensions.h"
19 
20 namespace {
21 
22 // Setting this limit to 0 practically forces sync at every submit.
23 constexpr uint32_t kSyncCpuMemoryLimitAtMemoryPressureCritical = 0;
24 
25 }  // namespace
26 
27 namespace viz {
28 
29 // static
30 scoped_refptr<VulkanInProcessContextProvider>
Create(gpu::VulkanImplementation * vulkan_implementation,uint32_t heap_memory_limit,uint32_t sync_cpu_memory_limit,const gpu::GPUInfo * gpu_info,base::TimeDelta cooldown_duration_at_memory_pressure_critical)31 VulkanInProcessContextProvider::Create(
32     gpu::VulkanImplementation* vulkan_implementation,
33     uint32_t heap_memory_limit,
34     uint32_t sync_cpu_memory_limit,
35     const gpu::GPUInfo* gpu_info,
36     base::TimeDelta cooldown_duration_at_memory_pressure_critical) {
37   scoped_refptr<VulkanInProcessContextProvider> context_provider(
38       new VulkanInProcessContextProvider(
39           vulkan_implementation, heap_memory_limit, sync_cpu_memory_limit,
40           cooldown_duration_at_memory_pressure_critical));
41   if (!context_provider->Initialize(gpu_info))
42     return nullptr;
43   return context_provider;
44 }
45 
VulkanInProcessContextProvider(gpu::VulkanImplementation * vulkan_implementation,uint32_t heap_memory_limit,uint32_t sync_cpu_memory_limit,base::TimeDelta cooldown_duration_at_memory_pressure_critical)46 VulkanInProcessContextProvider::VulkanInProcessContextProvider(
47     gpu::VulkanImplementation* vulkan_implementation,
48     uint32_t heap_memory_limit,
49     uint32_t sync_cpu_memory_limit,
50     base::TimeDelta cooldown_duration_at_memory_pressure_critical)
51     : vulkan_implementation_(vulkan_implementation),
52       heap_memory_limit_(heap_memory_limit),
53       sync_cpu_memory_limit_(sync_cpu_memory_limit),
54       cooldown_duration_at_memory_pressure_critical_(
55           cooldown_duration_at_memory_pressure_critical) {
56   memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>(
57       FROM_HERE,
58       base::BindRepeating(&VulkanInProcessContextProvider::OnMemoryPressure,
59                           base::Unretained(this)));
60 }
61 
~VulkanInProcessContextProvider()62 VulkanInProcessContextProvider::~VulkanInProcessContextProvider() {
63   Destroy();
64 }
65 
Initialize(const gpu::GPUInfo * gpu_info)66 bool VulkanInProcessContextProvider::Initialize(
67     const gpu::GPUInfo* gpu_info) {
68   DCHECK(!device_queue_);
69 
70   const auto& instance_extensions = vulkan_implementation_->GetVulkanInstance()
71                                         ->vulkan_info()
72                                         .enabled_instance_extensions;
73 
74   uint32_t flags = gpu::VulkanDeviceQueue::GRAPHICS_QUEUE_FLAG;
75   constexpr base::StringPiece surface_extension_name(
76       VK_KHR_SURFACE_EXTENSION_NAME);
77   for (const auto* extension : instance_extensions) {
78     if (surface_extension_name == extension) {
79       flags |= gpu::VulkanDeviceQueue::PRESENTATION_SUPPORT_QUEUE_FLAG;
80       break;
81     }
82   }
83 
84   device_queue_ = gpu::CreateVulkanDeviceQueue(vulkan_implementation_, flags,
85                                                gpu_info, heap_memory_limit_);
86   if (!device_queue_)
87     return false;
88 
89   return true;
90 }
91 
InitializeGrContext(const GrContextOptions & context_options)92 bool VulkanInProcessContextProvider::InitializeGrContext(
93     const GrContextOptions& context_options) {
94   GrVkBackendContext backend_context;
95   backend_context.fInstance = device_queue_->GetVulkanInstance();
96   backend_context.fPhysicalDevice = device_queue_->GetVulkanPhysicalDevice();
97   backend_context.fDevice = device_queue_->GetVulkanDevice();
98   backend_context.fQueue = device_queue_->GetVulkanQueue();
99   backend_context.fGraphicsQueueIndex = device_queue_->GetVulkanQueueIndex();
100   backend_context.fMaxAPIVersion = vulkan_implementation_->GetVulkanInstance()
101                                        ->vulkan_info()
102                                        .used_api_version;
103   backend_context.fMemoryAllocator =
104       gpu::CreateGrVkMemoryAllocator(device_queue_.get());
105 
106   GrVkGetProc get_proc = [](const char* proc_name, VkInstance instance,
107                             VkDevice device) {
108     if (device) {
109       if (std::strcmp("vkQueueSubmit", proc_name) == 0)
110         return reinterpret_cast<PFN_vkVoidFunction>(&gpu::QueueSubmitHook);
111       return vkGetDeviceProcAddr(device, proc_name);
112     }
113     return vkGetInstanceProcAddr(instance, proc_name);
114   };
115 
116   const auto& instance_extensions = vulkan_implementation_->GetVulkanInstance()
117                                         ->vulkan_info()
118                                         .enabled_instance_extensions;
119 
120   std::vector<const char*> device_extensions;
121   device_extensions.reserve(device_queue_->enabled_extensions().size());
122   for (const auto& extension : device_queue_->enabled_extensions())
123     device_extensions.push_back(extension.data());
124   GrVkExtensions gr_extensions;
125   gr_extensions.init(get_proc,
126                      vulkan_implementation_->GetVulkanInstance()->vk_instance(),
127                      device_queue_->GetVulkanPhysicalDevice(),
128                      instance_extensions.size(), instance_extensions.data(),
129                      device_extensions.size(), device_extensions.data());
130   backend_context.fVkExtensions = &gr_extensions;
131   backend_context.fDeviceFeatures2 =
132       &device_queue_->enabled_device_features_2();
133   backend_context.fGetProc = get_proc;
134   backend_context.fProtectedContext =
135       vulkan_implementation_->enforce_protected_memory() ? GrProtected::kYes
136                                                          : GrProtected::kNo;
137 
138   gr_context_ = GrDirectContext::MakeVulkan(backend_context, context_options);
139 
140   return gr_context_ != nullptr;
141 }
142 
Destroy()143 void VulkanInProcessContextProvider::Destroy() {
144   if (device_queue_) {
145     // Destroy |fence_helper| will wait idle on the device queue, and then run
146     // all enqueued cleanup tasks.
147     auto* fence_helper = device_queue_->GetFenceHelper();
148     fence_helper->Destroy();
149   }
150 
151   if (gr_context_) {
152     // releaseResourcesAndAbandonContext() will wait on GPU to finish all works,
153     // execute pending flush done callbacks and release all resources.
154     gr_context_->releaseResourcesAndAbandonContext();
155     gr_context_.reset();
156   }
157 
158   if (device_queue_) {
159     device_queue_->Destroy();
160     device_queue_.reset();
161   }
162 }
163 
164 gpu::VulkanImplementation*
GetVulkanImplementation()165 VulkanInProcessContextProvider::GetVulkanImplementation() {
166   return vulkan_implementation_;
167 }
168 
GetDeviceQueue()169 gpu::VulkanDeviceQueue* VulkanInProcessContextProvider::GetDeviceQueue() {
170   return device_queue_.get();
171 }
172 
GetGrContext()173 GrDirectContext* VulkanInProcessContextProvider::GetGrContext() {
174   return gr_context_.get();
175 }
176 
177 GrVkSecondaryCBDrawContext*
GetGrSecondaryCBDrawContext()178 VulkanInProcessContextProvider::GetGrSecondaryCBDrawContext() {
179   return nullptr;
180 }
181 
EnqueueSecondaryCBSemaphores(std::vector<VkSemaphore> semaphores)182 void VulkanInProcessContextProvider::EnqueueSecondaryCBSemaphores(
183     std::vector<VkSemaphore> semaphores) {
184   NOTREACHED();
185 }
186 
EnqueueSecondaryCBPostSubmitTask(base::OnceClosure closure)187 void VulkanInProcessContextProvider::EnqueueSecondaryCBPostSubmitTask(
188     base::OnceClosure closure) {
189   NOTREACHED();
190 }
191 
GetSyncCpuMemoryLimit() const192 base::Optional<uint32_t> VulkanInProcessContextProvider::GetSyncCpuMemoryLimit()
193     const {
194   // Return false to indicate that there's no limit.
195   if (!sync_cpu_memory_limit_)
196     return base::Optional<uint32_t>();
197   return base::TimeTicks::Now() < critical_memory_pressure_expiration_time_
198              ? base::Optional<uint32_t>(
199                    kSyncCpuMemoryLimitAtMemoryPressureCritical)
200              : base::Optional<uint32_t>(sync_cpu_memory_limit_);
201 }
202 
OnMemoryPressure(base::MemoryPressureListener::MemoryPressureLevel level)203 void VulkanInProcessContextProvider::OnMemoryPressure(
204     base::MemoryPressureListener::MemoryPressureLevel level) {
205   if (level != base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL)
206     return;
207 
208   critical_memory_pressure_expiration_time_ =
209       base::TimeTicks::Now() + cooldown_duration_at_memory_pressure_critical_;
210 }
211 
212 }  // namespace viz
213