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