Flutter Engine
The Flutter Engine
context_mtl.mm
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 <memory>
8
9#include "flutter/fml/concurrent_message_loop.h"
10#include "flutter/fml/file.h"
11#include "flutter/fml/logging.h"
12#include "flutter/fml/paths.h"
13#include "flutter/fml/synchronization/sync_switch.h"
19
20namespace impeller {
21
22static bool DeviceSupportsFramebufferFetch(id<MTLDevice> device) {
23 // The iOS simulator lies about supporting framebuffer fetch.
24#if FML_OS_IOS_SIMULATOR
25 return false;
26#else // FML_OS_IOS_SIMULATOR
27
28 if (@available(macOS 10.15, iOS 13, tvOS 13, *)) {
29 return [device supportsFamily:MTLGPUFamilyApple2];
30 }
31 // According to
32 // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf , Apple2
33 // corresponds to iOS GPU family 2, which supports A8 devices.
34#if FML_OS_IOS
35 return [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1];
36#else
37 return false;
38#endif // FML_OS_IOS
39#endif // FML_OS_IOS_SIMULATOR
40}
41
42static bool DeviceSupportsComputeSubgroups(id<MTLDevice> device) {
43 bool supports_subgroups = false;
44 // Refer to the "SIMD-scoped reduction operations" feature in the table
45 // below: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
46 if (@available(ios 13.0, tvos 13.0, macos 10.15, *)) {
47 supports_subgroups = [device supportsFamily:MTLGPUFamilyApple7] ||
48 [device supportsFamily:MTLGPUFamilyMac2];
49 }
50 return supports_subgroups;
51}
52
53static std::unique_ptr<Capabilities> InferMetalCapabilities(
54 id<MTLDevice> device,
55 PixelFormat color_format) {
56 return CapabilitiesBuilder()
58 .SetSupportsSSBO(true)
62 .SetDefaultColorFormat(color_format)
70 .Build();
71}
72
73ContextMTL::ContextMTL(
74 id<MTLDevice> device,
75 id<MTLCommandQueue> command_queue,
76 NSArray<id<MTLLibrary>>* shader_libraries,
77 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
78 std::optional<PixelFormat> pixel_format_override)
79 : device_(device),
80 command_queue_(command_queue),
81 is_gpu_disabled_sync_switch_(std::move(is_gpu_disabled_sync_switch)) {
82 // Validate device.
83 if (!device_) {
84 VALIDATION_LOG << "Could not set up valid Metal device.";
85 return;
86 }
87
88 sync_switch_observer_.reset(new SyncSwitchObserver(*this));
89 is_gpu_disabled_sync_switch_->AddObserver(sync_switch_observer_.get());
90
91 // Setup the shader library.
92 {
93 if (shader_libraries == nil) {
94 VALIDATION_LOG << "Shader libraries were null.";
95 return;
96 }
97
98 // std::make_shared disallowed because of private friend ctor.
99 auto library = std::shared_ptr<ShaderLibraryMTL>(
100 new ShaderLibraryMTL(shader_libraries));
101 if (!library->IsValid()) {
102 VALIDATION_LOG << "Could not create valid Metal shader library.";
103 return;
104 }
105 shader_library_ = std::move(library);
106 }
107
108 // Setup the pipeline library.
109 {
110 pipeline_library_ =
111 std::shared_ptr<PipelineLibraryMTL>(new PipelineLibraryMTL(device_));
112 }
113
114 // Setup the sampler library.
115 {
116 sampler_library_ =
117 std::shared_ptr<SamplerLibraryMTL>(new SamplerLibraryMTL(device_));
118 }
119
120 // Setup the resource allocator.
121 {
122 resource_allocator_ = std::shared_ptr<AllocatorMTL>(
123 new AllocatorMTL(device_, "Impeller Permanents Allocator"));
124 if (!resource_allocator_) {
125 VALIDATION_LOG << "Could not set up the resource allocator.";
126 return;
127 }
128 }
129
130 device_capabilities_ =
131 InferMetalCapabilities(device_, pixel_format_override.has_value()
132 ? pixel_format_override.value()
134 command_queue_ip_ = std::make_shared<CommandQueue>();
135#ifdef IMPELLER_DEBUG
136 gpu_tracer_ = std::make_shared<GPUTracerMTL>();
137#endif // IMPELLER_DEBUG
138 is_valid_ = true;
139}
140
141static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFilePaths(
142 id<MTLDevice> device,
143 const std::vector<std::string>& libraries_paths) {
144 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
145 for (const auto& library_path : libraries_paths) {
146 if (!fml::IsFile(library_path)) {
147 VALIDATION_LOG << "Shader library does not exist at path '"
148 << library_path << "'";
149 return nil;
150 }
151 NSError* shader_library_error = nil;
152 auto library = [device newLibraryWithFile:@(library_path.c_str())
153 error:&shader_library_error];
154 if (!library) {
155 FML_LOG(ERROR) << "Could not create shader library: "
156 << shader_library_error.localizedDescription.UTF8String;
157 return nil;
158 }
159 [found_libraries addObject:library];
160 }
161 return found_libraries;
162}
163
164static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFileData(
165 id<MTLDevice> device,
166 const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data,
167 const std::string& label) {
168 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
169 for (const auto& library_data : libraries_data) {
170 if (library_data == nullptr) {
171 FML_LOG(ERROR) << "Shader library data was null.";
172 return nil;
173 }
174
175 __block auto data = library_data;
176
177 auto dispatch_data =
178 ::dispatch_data_create(library_data->GetMapping(), // buffer
179 library_data->GetSize(), // size
180 dispatch_get_main_queue(), // queue
181 ^() {
182 // We just need a reference.
183 data.reset();
184 } // destructor
185 );
186 if (!dispatch_data) {
187 FML_LOG(ERROR) << "Could not wrap shader data in dispatch data.";
188 return nil;
189 }
190
191 NSError* shader_library_error = nil;
192 auto library = [device newLibraryWithData:dispatch_data
193 error:&shader_library_error];
194 if (!library) {
195 FML_LOG(ERROR) << "Could not create shader library: "
196 << shader_library_error.localizedDescription.UTF8String;
197 return nil;
198 }
199 if (!label.empty()) {
200 library.label = @(label.c_str());
201 }
202 [found_libraries addObject:library];
203 }
204 return found_libraries;
205}
206
207static id<MTLDevice> CreateMetalDevice() {
208 return ::MTLCreateSystemDefaultDevice();
209}
210
211static id<MTLCommandQueue> CreateMetalCommandQueue(id<MTLDevice> device) {
212 auto command_queue = device.newCommandQueue;
213 if (!command_queue) {
214 VALIDATION_LOG << "Could not set up the command queue.";
215 return nullptr;
216 }
217 command_queue.label = @"Impeller Command Queue";
218 return command_queue;
219}
220
221std::shared_ptr<ContextMTL> ContextMTL::Create(
222 const std::vector<std::string>& shader_library_paths,
223 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
224 auto device = CreateMetalDevice();
225 auto command_queue = CreateMetalCommandQueue(device);
226 if (!command_queue) {
227 return nullptr;
228 }
229 auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
230 device, command_queue,
231 MTLShaderLibraryFromFilePaths(device, shader_library_paths),
232 std::move(is_gpu_disabled_sync_switch)));
233 if (!context->IsValid()) {
234 FML_LOG(ERROR) << "Could not create Metal context.";
235 return nullptr;
236 }
237 return context;
238}
239
240std::shared_ptr<ContextMTL> ContextMTL::Create(
241 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
242 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
243 const std::string& library_label,
244 std::optional<PixelFormat> pixel_format_override) {
245 auto device = CreateMetalDevice();
246 auto command_queue = CreateMetalCommandQueue(device);
247 if (!command_queue) {
248 return nullptr;
249 }
250 auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
251 device, command_queue,
252 MTLShaderLibraryFromFileData(device, shader_libraries_data,
253 library_label),
254 std::move(is_gpu_disabled_sync_switch), pixel_format_override));
255 if (!context->IsValid()) {
256 FML_LOG(ERROR) << "Could not create Metal context.";
257 return nullptr;
258 }
259 return context;
260}
261
262std::shared_ptr<ContextMTL> ContextMTL::Create(
263 id<MTLDevice> device,
264 id<MTLCommandQueue> command_queue,
265 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
266 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
267 const std::string& library_label) {
268 auto context = std::shared_ptr<ContextMTL>(
269 new ContextMTL(device, command_queue,
270 MTLShaderLibraryFromFileData(device, shader_libraries_data,
271 library_label),
272 std::move(is_gpu_disabled_sync_switch)));
273 if (!context->IsValid()) {
274 FML_LOG(ERROR) << "Could not create Metal context.";
275 return nullptr;
276 }
277 return context;
278}
279
280ContextMTL::~ContextMTL() {
281 is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
282}
283
284Context::BackendType ContextMTL::GetBackendType() const {
286}
287
288// |Context|
289std::string ContextMTL::DescribeGpuModel() const {
290 return std::string([[device_ name] UTF8String]);
291}
292
293// |Context|
294bool ContextMTL::IsValid() const {
295 return is_valid_;
296}
297
298// |Context|
299std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary() const {
300 return shader_library_;
301}
302
303// |Context|
304std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary() const {
305 return pipeline_library_;
306}
307
308// |Context|
309std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary() const {
310 return sampler_library_;
311}
312
313// |Context|
314std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBuffer() const {
315 return CreateCommandBufferInQueue(command_queue_);
316}
317
318// |Context|
320
321#ifdef IMPELLER_DEBUG
322std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer() const {
323 return gpu_tracer_;
324}
325#endif // IMPELLER_DEBUG
326
327std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
328 const {
329 return is_gpu_disabled_sync_switch_;
330}
331
332std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
333 id<MTLCommandQueue> queue) const {
334 if (!IsValid()) {
335 return nullptr;
336 }
337
338 auto buffer = std::shared_ptr<CommandBufferMTL>(
339 new CommandBufferMTL(weak_from_this(), queue));
340 if (!buffer->IsValid()) {
341 return nullptr;
342 }
343 return buffer;
344}
345
346std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator() const {
347 return resource_allocator_;
348}
349
350id<MTLDevice> ContextMTL::GetMTLDevice() const {
351 return device_;
352}
353
354const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities() const {
355 return device_capabilities_;
356}
357
358void ContextMTL::SetCapabilities(
359 const std::shared_ptr<const Capabilities>& capabilities) {
360 device_capabilities_ = capabilities;
361}
362
363// |Context|
364bool ContextMTL::UpdateOffscreenLayerPixelFormat(PixelFormat format) {
365 device_capabilities_ = InferMetalCapabilities(device_, format);
366 return true;
367}
368
369id<MTLCommandBuffer> ContextMTL::CreateMTLCommandBuffer(
370 const std::string& label) const {
371 auto buffer = [command_queue_ commandBuffer];
372 if (!label.empty()) {
373 [buffer setLabel:@(label.data())];
374 }
375 return buffer;
376}
377
378void ContextMTL::StoreTaskForGPU(const std::function<void()>& task) {
379 tasks_awaiting_gpu_.emplace_back(task);
380 while (tasks_awaiting_gpu_.size() > kMaxTasksAwaitingGPU) {
381 tasks_awaiting_gpu_.front()();
382 tasks_awaiting_gpu_.pop_front();
383 }
384}
385
386void ContextMTL::FlushTasksAwaitingGPU() {
387 for (const auto& task : tasks_awaiting_gpu_) {
388 task();
389 }
390 tasks_awaiting_gpu_.clear();
391}
392
393ContextMTL::SyncSwitchObserver::SyncSwitchObserver(ContextMTL& parent)
394 : parent_(parent) {}
395
396void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(bool new_is_disabled) {
397 if (!new_is_disabled) {
398 parent_.FlushTasksAwaitingGPU();
399 }
400}
401
402// |Context|
403std::shared_ptr<CommandQueue> ContextMTL::GetCommandQueue() const {
404 return command_queue_ip_;
405}
406
407} // namespace impeller
static sk_sp< Effect > Create()
Definition: RefCntTest.cpp:117
CapabilitiesBuilder & SetDefaultColorFormat(PixelFormat value)
CapabilitiesBuilder & SetSupportsComputeSubgroups(bool value)
CapabilitiesBuilder & SetSupportsTextureToTextureBlits(bool value)
CapabilitiesBuilder & SetDefaultStencilFormat(PixelFormat value)
CapabilitiesBuilder & SetSupportsDeviceTransientTextures(bool value)
CapabilitiesBuilder & SetSupportsFramebufferFetch(bool value)
CapabilitiesBuilder & SetSupportsDecalSamplerAddressMode(bool value)
CapabilitiesBuilder & SetSupportsOffscreenMSAA(bool value)
CapabilitiesBuilder & SetSupportsSSBO(bool value)
CapabilitiesBuilder & SetDefaultGlyphAtlasFormat(PixelFormat value)
CapabilitiesBuilder & SetSupportsCompute(bool value)
std::unique_ptr< Capabilities > Build()
CapabilitiesBuilder & SetDefaultDepthStencilFormat(PixelFormat value)
CapabilitiesBuilder & SetSupportsReadFromResolve(bool value)
std::shared_ptr< CommandQueue > GetCommandQueue() const override
Return the graphics queue for submitting command buffers.
Definition: context_mtl.mm:403
@ kMetal
Definition: embedder.h:85
VkDevice device
Definition: main.cc:53
VkQueue queue
Definition: main.cc:55
const uint8_t uint32_t uint32_t GError ** error
uint32_t uint32_t * format
#define FML_LOG(severity)
Definition: logging.h:82
Dart_NativeFunction function
Definition: fuchsia.cc:51
SK_API sk_sp< SkSurface > ios(9.0)
SK_API sk_sp< SkSurface > tvos(9.0))
static void Shutdown(Dart_NativeArguments args)
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
bool IsFile(const std::string &path)
Definition: file_posix.cc:146
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFilePaths(id< MTLDevice > device, const std::vector< std::string > &libraries_paths)
Definition: context_mtl.mm:141
static id< MTLCommandQueue > CreateMetalCommandQueue(id< MTLDevice > device)
Definition: context_mtl.mm:211
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:99
static id< MTLDevice > CreateMetalDevice()
Definition: context_mtl.mm:207
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFileData(id< MTLDevice > device, const std::vector< std::shared_ptr< fml::Mapping > > &libraries_data, const std::string &label)
Definition: context_mtl.mm:164
static bool DeviceSupportsComputeSubgroups(id< MTLDevice > device)
Definition: context_mtl.mm:42
static id< MTLCommandBuffer > CreateCommandBuffer(id< MTLCommandQueue > queue)
static bool DeviceSupportsFramebufferFetch(id< MTLDevice > device)
Definition: context_mtl.mm:22
static std::unique_ptr< Capabilities > InferMetalCapabilities(id< MTLDevice > device, PixelFormat color_format)
Definition: context_mtl.mm:53
Definition: ref_ptr.h:256
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
#define ERROR(message)
Definition: elf_loader.cc:260
#define VALIDATION_LOG
Definition: validation.h:73