Flutter Engine
The 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
14#include "flutter/fml/trace_event.h"
15#include "flutter/vulkan/vulkan_skia_proc_table.h"
17
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 = {
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.
93 << "VulkanSurfaceProducer: Device proc addresses have not been set up.";
94 return false;
95 }
96
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 uint32_t skia_features = 0;
116 if (!logical_device_->GetPhysicalDeviceFeaturesSkia(&skia_features)) {
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 GrVkBackendContext 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.fMinAPIVersion = application_->GetAPIVersion();
136 backend_context.fMaxAPIVersion = application_->GetAPIVersion();
137 backend_context.fFeatures = skia_features;
138 backend_context.fGetProc = std::move(getProc);
139 backend_context.fOwnsInstanceAndDevice = false;
140 backend_context.fMemoryAllocator = memory_allocator_;
141
142 // The memory_requirements_2 extension is required on Fuchsia as the AMD
143 // memory allocator used by Skia benefit from it.
144 const char* device_extensions[] = {
146 };
147 const int device_extensions_count =
148 sizeof(device_extensions) / sizeof(device_extensions[0]);
149 GrVkExtensions vk_extensions;
150 vk_extensions.init(backend_context.fGetProc, backend_context.fInstance,
151 backend_context.fPhysicalDevice, 0, nullptr,
152 device_extensions_count, device_extensions);
153 backend_context.fVkExtensions = &vk_extensions;
156
157 context_ = GrDirectContexts::MakeVulkan(backend_context, options);
158
159 if (context_ == nullptr) {
161 << "VulkanSurfaceProducer: Failed to create GrDirectContext.";
162 return false;
163 }
164
165 // Use local limits specified in this file above instead of flutter defaults.
166 context_->setResourceCacheLimit(kGrCacheMaxByteSize);
167
168 surface_pool_ = std::make_unique<VulkanSurfacePool>(*this, context_);
169
170 return true;
171}
172
174 std::vector<std::unique_ptr<SurfaceProducerSurface>> surfaces) {
175 TRACE_EVENT0("flutter", "VulkanSurfaceProducer::SubmitSurfaces");
176
177 // Do a single flush for all canvases derived from the context.
178 {
179 TRACE_EVENT0("flutter", "GrDirectContext::flushAndSignalSemaphores");
180 context_->flushAndSubmit();
181 }
182
183 if (!TransitionSurfacesToExternal(surfaces))
184 FML_LOG(ERROR) << "TransitionSurfacesToExternal failed";
185
186 // Submit surface
187 for (auto& surface : surfaces) {
188 SubmitSurface(std::move(surface));
189 }
190
191 // Buffer management.
192 surface_pool_->AgeAndCollectOldBuffers();
193
194 // If no further surface production has taken place for 10 frames (TODO:
195 // Don't hardcode refresh rate here), then shrink our surface pool to fit.
196 constexpr auto kShouldShrinkThreshold = zx::msec(10 * 16.67);
197 async::PostDelayedTask(
198 async_get_default_dispatcher(),
199 [self = weak_factory_.GetWeakPtr(), kShouldShrinkThreshold] {
200 if (!self) {
201 return;
202 }
203 auto time_since_last_produce =
204 async::Now(async_get_default_dispatcher()) -
205 self->last_produce_time_;
206 if (time_since_last_produce >= kShouldShrinkThreshold) {
207 self->surface_pool_->ShrinkToFit();
208 }
209 },
210 kShouldShrinkThreshold);
211}
212
213bool VulkanSurfaceProducer::TransitionSurfacesToExternal(
214 const std::vector<std::unique_ptr<SurfaceProducerSurface>>& surfaces) {
215 for (auto& surface : surfaces) {
216 auto vk_surface = static_cast<VulkanSurface*>(surface.get());
217 if (!vk_surface) {
218 continue;
219 }
220
221 vulkan::VulkanCommandBuffer* command_buffer =
222 vk_surface->GetCommandBuffer(logical_device_->GetCommandPool());
223 if (!command_buffer->Begin())
224 return false;
225
227 vk_surface->GetSkiaSurface().get(),
228 SkSurfaces::BackendHandleAccess::kFlushRead);
229 if (!backendRT.isValid()) {
230 return false;
231 }
232 GrVkImageInfo imageInfo;
233 if (!GrBackendRenderTargets::GetVkImageInfo(backendRT, &imageInfo)) {
234 return false;
235 }
236
237 VkImageMemoryBarrier image_barrier = {
239 .pNext = nullptr,
241 .dstAccessMask = 0,
242 .oldLayout = imageInfo.fImageLayout,
243 // Understand why this is causing issues on Intel. TODO(fxb/53449)
244#if defined(__aarch64__)
245 .newLayout = imageInfo.fImageLayout,
246#else
247 .newLayout = VK_IMAGE_LAYOUT_GENERAL,
248#endif
249 .srcQueueFamilyIndex = 0,
250 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR,
251 .image = vk_surface->GetVkImage(),
252 .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}};
253
254 if (!command_buffer->InsertPipelineBarrier(
257 0, // dependencyFlags
258 0, nullptr, // memory barriers
259 0, nullptr, // buffer barriers
260 1, &image_barrier))
261 return false;
262
264 image_barrier.newLayout);
265
266 if (!command_buffer->End())
267 return false;
268
269 if (!logical_device_->QueueSubmit(
270 {}, {}, {vk_surface->GetAcquireVkSemaphore()},
271 {command_buffer->Handle()}, vk_surface->GetCommandBufferFence()))
272 return false;
273 }
274 return true;
275}
276
277std::unique_ptr<SurfaceProducerSurface> VulkanSurfaceProducer::ProduceSurface(
278 const SkISize& size) {
279 FML_CHECK(valid_);
280 last_produce_time_ = async::Now(async_get_default_dispatcher());
281 return surface_pool_->AcquireSurface(size);
282}
283
284void VulkanSurfaceProducer::SubmitSurface(
285 std::unique_ptr<SurfaceProducerSurface> surface) {
286 FML_CHECK(valid_);
287 surface_pool_->SubmitSurface(std::move(surface));
288}
289
290std::unique_ptr<SurfaceProducerSurface>
291VulkanSurfaceProducer::ProduceOffscreenSurface(const SkISize& size) {
292 return surface_pool_->CreateSurface(size);
293}
294
295} // namespace flutter_runner
const char * options
void flushAndSubmit(GrSyncCpu sync=GrSyncCpu::kNo)
void setResourceCacheLimit(size_t maxResourceBytes)
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
void init(VulkanGetProc, VkInstance, VkPhysicalDevice, uint32_t instanceExtensionCount, const char *const *instanceExtensions, uint32_t deviceExtensionCount, const char *const *deviceExtensions)
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:49
#define FATAL(error)
#define FML_LOG(severity)
Definition logging.h:82
#define FML_CHECK(condition)
Definition logging.h:85
#define FML_DCHECK(condition)
Definition logging.h:103
SK_API bool GetVkImageInfo(const GrBackendRenderTarget &, GrVkImageInfo *)
SK_API void SetVkImageLayout(GrBackendRenderTarget *, VkImageLayout)
SK_API sk_sp< GrDirectContext > MakeVulkan(const GrVkBackendContext &, const GrContextOptions &)
SK_API GrBackendRenderTarget GetBackendRenderTarget(SkSurface *, BackendHandleAccess)
GrVkGetProc CreateSkiaGetProc(const fml::RefPtr< vulkan::VulkanProcTable > &vk)
const skgpu::VulkanExtensions * fVkExtensions
VkPhysicalDevice fPhysicalDevice
skgpu::VulkanGetProc fGetProc
sk_sp< skgpu::VulkanMemoryAllocator > fMemoryAllocator
VkImageLayout fImageLayout
Definition GrVkTypes.h:29
VkStructureType sType
VkImageLayout newLayout
#define ERROR(message)
#define TRACE_EVENT0(category_group, name)
@ VK_IMAGE_LAYOUT_GENERAL
@ VK_IMAGE_ASPECT_COLOR_BIT
#define VK_MAKE_VERSION(major, minor, patch)
Definition vulkan_core.h:78
#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME
VkResult
@ VK_SUCCESS
@ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
#define VK_QUEUE_FAMILY_EXTERNAL_KHR
#define VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME
@ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT
@ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
@ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER
#define VK_CALL_LOG_ERROR(expression)