Flutter Engine
 
Loading...
Searching...
No Matches
vulkan_surface_producer.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter 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
6
7#include <lib/async/cpp/task.h>
8#include <lib/async/default.h>
9
10#include <memory>
11#include <string>
12#include <vector>
13
17
18#include "third_party/skia/include/core/SkSurface.h"
19#include "third_party/skia/include/gpu/ganesh/GrBackendSemaphore.h"
20#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
21#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
22#include "third_party/skia/include/gpu/ganesh/vk/GrVkBackendSurface.h"
23#include "third_party/skia/include/gpu/ganesh/vk/GrVkDirectContext.h"
24#include "third_party/skia/include/gpu/ganesh/vk/GrVkTypes.h"
25#include "third_party/skia/include/gpu/vk/VulkanBackendContext.h"
26#include "third_party/skia/include/gpu/vk/VulkanExtensions.h"
27
28namespace flutter_runner {
29
30namespace {
31
32// Tuning advice:
33// If you see the following 3 things happening simultaneously in a trace:
34// * Over budget ("flutter", "GPURasterizer::Draw") durations
35// * Many ("skia", "GrGpu::createTexture") events within the
36// "GPURasterizer::Draw"s
37// * The Skia GPU resource cache is full, as indicated by the
38// "SkiaCacheBytes" field in the ("flutter", "SurfacePool") trace counter
39// (compare it to the bytes value here)
40// then you should consider increasing the size of the GPU resource cache.
41constexpr size_t kGrCacheMaxByteSize = 1024 * 600 * 12 * 4;
42
43} // namespace
44
46 valid_ = Initialize();
47
48 if (!valid_) {
49 FML_LOG(FATAL) << "VulkanSurfaceProducer: Initialization failed";
50 }
51}
52
54 // Make sure queue is idle before we start destroying surfaces
55 if (valid_) {
56 VkResult wait_result = VK_CALL_LOG_ERROR(
57 vk_->QueueWaitIdle(logical_device_->GetQueueHandle()));
58 FML_DCHECK(wait_result == VK_SUCCESS);
59 }
60};
61
62bool VulkanSurfaceProducer::Initialize() {
63 vk_ = fml::MakeRefCounted<vulkan::VulkanProcTable>();
64
65 std::vector<std::string> extensions = {
66 VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME,
67 };
68
69 // On Fuchsia, the validation layers need to be packaged as part of the
70 // flutter_runner in order to work. As a result, we can use the presence
71 // of the layers to mean that we want the layers enabled.
72 application_ = std::make_unique<vulkan::VulkanApplication>(
73 *vk_, "FlutterRunner", std::move(extensions), VK_MAKE_VERSION(1, 0, 0),
74 VK_MAKE_VERSION(1, 1, 0), true /* enable_validation_layers */);
75
76 if (!application_->IsValid() || !vk_->AreInstanceProcsSetup()) {
77 // Make certain the application instance was created and it set up the
78 // instance proc table entries.
79 FML_LOG(ERROR) << "VulkanSurfaceProducer: Instance proc addresses have not "
80 "been set up.";
81 return false;
82 }
83
84 // Create the device.
85
86 logical_device_ = application_->AcquireFirstCompatibleLogicalDevice();
87
88 if (logical_device_ == nullptr || !logical_device_->IsValid() ||
89 !vk_->AreDeviceProcsSetup()) {
90 // Make certain the device was created and it set up the device proc table
91 // entries.
92 FML_LOG(ERROR)
93 << "VulkanSurfaceProducer: Device proc addresses have not been set up.";
94 return false;
95 }
96
98 FML_LOG(ERROR)
99 << "VulkanSurfaceProducer: Failed to acquire mandatory proc addresses.";
100 return false;
101 }
102
103 if (!vk_->IsValid()) {
104 FML_LOG(ERROR) << "VulkanSurfaceProducer: VulkanProcTable invalid";
105 return false;
106 }
107
108 auto getProc = CreateSkiaGetProc(vk_);
109
110 if (getProc == nullptr) {
111 FML_LOG(ERROR) << "VulkanSurfaceProducer: Failed to create skia getProc.";
112 return false;
113 }
114
115 VkPhysicalDeviceFeatures features;
116 if (!logical_device_->GetPhysicalDeviceFeatures(&features)) {
117 FML_LOG(ERROR)
118 << "VulkanSurfaceProducer: Failed to get physical device features.";
119
120 return false;
121 }
122
124 application_->GetAPIVersion(), application_->GetInstance(),
125 logical_device_->GetPhysicalDeviceHandle(), logical_device_->GetHandle(),
126 vk_, true);
127
128 skgpu::VulkanBackendContext backend_context;
129 backend_context.fInstance = application_->GetInstance();
130 backend_context.fPhysicalDevice = logical_device_->GetPhysicalDeviceHandle();
131 backend_context.fDevice = logical_device_->GetHandle();
132 backend_context.fQueue = logical_device_->GetQueueHandle();
133 backend_context.fGraphicsQueueIndex =
134 logical_device_->GetGraphicsQueueIndex();
135 backend_context.fMaxAPIVersion = application_->GetAPIVersion();
136 backend_context.fDeviceFeatures = &features;
137 backend_context.fGetProc = std::move(getProc);
138 backend_context.fMemoryAllocator = memory_allocator_;
139
140 // The memory_requirements_2 extension is required on Fuchsia as the AMD
141 // memory allocator used by Skia benefit from it.
142 const char* device_extensions[] = {
143 VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
144 };
145 const int device_extensions_count =
146 sizeof(device_extensions) / sizeof(device_extensions[0]);
147 skgpu::VulkanExtensions vk_extensions;
148 vk_extensions.init(backend_context.fGetProc, backend_context.fInstance,
149 backend_context.fPhysicalDevice, 0, nullptr,
150 device_extensions_count, device_extensions);
151 backend_context.fVkExtensions = &vk_extensions;
152 GrContextOptions options;
153 options.fReduceOpsTaskSplitting = GrContextOptions::Enable::kNo;
154
155 context_ = GrDirectContexts::MakeVulkan(backend_context, options);
156
157 if (context_ == nullptr) {
158 FML_LOG(ERROR)
159 << "VulkanSurfaceProducer: Failed to create GrDirectContext.";
160 return false;
161 }
162
163 // Use local limits specified in this file above instead of flutter defaults.
164 context_->setResourceCacheLimit(kGrCacheMaxByteSize);
165
166 surface_pool_ = std::make_unique<VulkanSurfacePool>(*this, context_);
167
168 return true;
169}
170
172 std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces) {
173 TRACE_EVENT0("flutter", "VulkanSurfaceProducer::SubmitSurfaces");
174
175 // Do a single flush for all canvases derived from the context.
176 {
177 TRACE_EVENT0("flutter", "GrDirectContext::flushAndSignalSemaphores");
178 context_->flushAndSubmit();
179 }
180
181 if (!TransitionSurfacesToExternal(surfaces))
182 FML_LOG(ERROR) << "TransitionSurfacesToExternal failed";
183
184 // Submit surface
185 for (auto& surface : surfaces) {
186 SubmitSurface(std::move(surface));
187 }
188
189 // Buffer management.
190 surface_pool_->AgeAndCollectOldBuffers();
191
192 // If no further surface production has taken place for 10 frames (TODO:
193 // Don't hardcode refresh rate here), then shrink our surface pool to fit.
194 constexpr auto kShouldShrinkThreshold = zx::msec(10 * 16.67);
195 async::PostDelayedTask(
196 async_get_default_dispatcher(),
197 [self = weak_factory_.GetWeakPtr(), kShouldShrinkThreshold] {
198 if (!self) {
199 return;
200 }
201 auto time_since_last_produce =
202 async::Now(async_get_default_dispatcher()) -
203 self->last_produce_time_;
204 if (time_since_last_produce >= kShouldShrinkThreshold) {
205 self->surface_pool_->ShrinkToFit();
206 }
207 },
208 kShouldShrinkThreshold);
209}
210
211bool VulkanSurfaceProducer::TransitionSurfacesToExternal(
212 const std::vector<std::unique_ptr<SurfaceProducerSurface>>& surfaces) {
213 for (auto& surface : surfaces) {
214 auto vk_surface = static_cast<VulkanSurface*>(surface.get());
215 if (!vk_surface) {
216 continue;
217 }
218
219 vulkan::VulkanCommandBuffer* command_buffer =
220 vk_surface->GetCommandBuffer(logical_device_->GetCommandPool());
221 if (!command_buffer->Begin())
222 return false;
223
224 GrBackendRenderTarget backendRT = SkSurfaces::GetBackendRenderTarget(
225 vk_surface->GetSkiaSurface().get(),
226 SkSurfaces::BackendHandleAccess::kFlushRead);
227 if (!backendRT.isValid()) {
228 return false;
229 }
230 GrVkImageInfo imageInfo;
231 if (!GrBackendRenderTargets::GetVkImageInfo(backendRT, &imageInfo)) {
232 return false;
233 }
234
235 VkImageMemoryBarrier image_barrier = {
236 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
237 .pNext = nullptr,
238 .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
239 .dstAccessMask = 0,
240 .oldLayout = imageInfo.fImageLayout,
241 // Understand why this is causing issues on Intel. TODO(fxb/53449)
242#if defined(__aarch64__)
243 .newLayout = imageInfo.fImageLayout,
244#else
245 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
246#endif
247 .srcQueueFamilyIndex = 0,
248 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR,
249 .image = vk_surface->GetVkImage(),
250 .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
251
252 if (!command_buffer->InsertPipelineBarrier(
253 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
254 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
255 0, // dependencyFlags
256 0, nullptr, // memory barriers
257 0, nullptr, // buffer barriers
258 1, &image_barrier))
259 return false;
260
261 GrBackendRenderTargets::SetVkImageLayout(&backendRT,
262 image_barrier.newLayout);
263
264 if (!command_buffer->End())
265 return false;
266
267 if (!logical_device_->QueueSubmit(
268 {}, {}, {vk_surface->GetAcquireVkSemaphore()},
269 {command_buffer->Handle()}, vk_surface->GetCommandBufferFence()))
270 return false;
271 }
272 return true;
273}
274
275std::unique_ptr<SurfaceProducerSurface> VulkanSurfaceProducer::ProduceSurface(
276 const SkISize& size) {
277 FML_CHECK(valid_);
278 last_produce_time_ = async::Now(async_get_default_dispatcher());
279 return surface_pool_->AcquireSurface(size);
280}
281
282void VulkanSurfaceProducer::SubmitSurface(
283 std::unique_ptr<SurfaceProducerSurface> surface) {
284 FML_CHECK(valid_);
285 surface_pool_->SubmitSurface(std::move(surface));
286}
287
288std::unique_ptr<SurfaceProducerSurface>
289VulkanSurfaceProducer::ProduceOffscreenSurface(const SkISize& size) {
290 return surface_pool_->CreateSurface(size);
291}
292
293} // namespace flutter_runner
static sk_sp< VulkanMemoryAllocator > Make(uint32_t vulkan_api_version, VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device, const fml::RefPtr< vulkan::VulkanProcTable > &vk, bool mustUseCoherentHostVisibleMemory)
void SubmitSurfaces(std::vector< std::unique_ptr< SurfaceProducerSurface > > surfaces) override
bool InsertPipelineBarrier(VkPipelineStageFlagBits src_stage_flags, VkPipelineStageFlagBits dest_stage_flags, uint32_t dependency_flags, uint32_t memory_barrier_count, const VkMemoryBarrier *memory_barriers, uint32_t buffer_memory_barrier_count, const VkBufferMemoryBarrier *buffer_memory_barriers, uint32_t image_memory_barrier_count, const VkImageMemoryBarrier *image_memory_barriers) const
bool HasAcquiredMandatoryProcAddresses() const
VkSurfaceKHR surface
Definition main.cc:65
#define FML_LOG(severity)
Definition logging.h:101
#define FML_CHECK(condition)
Definition logging.h:104
#define FML_DCHECK(condition)
Definition logging.h:122
skgpu::VulkanGetProc CreateSkiaGetProc(const fml::RefPtr< vulkan::VulkanProcTable > &vk)
#define TRACE_EVENT0(category_group, name)
#define VK_CALL_LOG_ERROR(expression)