| Flutter Impeller |

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
5 #include "impeller/renderer/backend/metal/context_mtl.h"
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"
14 #include "impeller/core/formats.h"
15 #include "impeller/core/sampler_descriptor.h"
16 #include "impeller/renderer/backend/metal/gpu_tracer_mtl.h"
17 #include "impeller/renderer/backend/metal/sampler_library_mtl.h"
18 #include "impeller/renderer/capabilities.h"
19
20 namespace impeller {
21
22 static 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
42 static 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
53 static std::unique_ptr<Capabilities> InferMetalCapabilities(
54 id<MTLDevice> device,
55 PixelFormat color_format) {
56 return CapabilitiesBuilder()
57 .SetSupportsOffscreenMSAA(true)
58 .SetSupportsSSBO(true)
59 .SetSupportsBufferToTextureBlits(true)
60 .SetSupportsTextureToTextureBlits(true)
61 .SetSupportsDecalSamplerAddressMode(true)
62 .SetSupportsFramebufferFetch(DeviceSupportsFramebufferFetch(device))
63 .SetDefaultColorFormat(color_format)
64 .SetDefaultStencilFormat(PixelFormat::kS8UInt)
65 .SetDefaultDepthStencilFormat(PixelFormat::kD32FloatS8UInt)
66 .SetSupportsCompute(true)
67 .SetSupportsComputeSubgroups(DeviceSupportsComputeSubgroups(device))
68 .SetSupportsReadFromResolve(true)
69 .SetSupportsDeviceTransientTextures(true)
70 .SetDefaultGlyphAtlasFormat(PixelFormat::kA8UNormInt)
71 .Build();
72 }
73
74 ContextMTL::ContextMTL(
75 id<MTLDevice> device,
76 id<MTLCommandQueue> command_queue,
77 NSArray<id<MTLLibrary>>* shader_libraries,
78 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch)
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_, PixelFormat::kB8G8R8A8UNormInt);
132 command_queue_ip_ = std::make_shared<CommandQueue>();
133 #ifdef IMPELLER_DEBUG
134 gpu_tracer_ = std::make_shared<GPUTracerMTL>();
135 #endif // IMPELLER_DEBUG
136 is_valid_ = true;
137 }
138
139 static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFilePaths(
140 id<MTLDevice> device,
141 const std::vector<std::string>& libraries_paths) {
142 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
143 for (const auto& library_path : libraries_paths) {
144 if (!fml::IsFile(library_path)) {
145 VALIDATION_LOG << "Shader library does not exist at path '"
146 << library_path << "'";
147 return nil;
148 }
149 NSError* shader_library_error = nil;
150 auto library = [device newLibraryWithFile:@(library_path.c_str())
151 error:&shader_library_error];
152 if (!library) {
153 FML_LOG(ERROR) << "Could not create shader library: "
154 << shader_library_error.localizedDescription.UTF8String;
155 return nil;
156 }
157 [found_libraries addObject:library];
158 }
159 return found_libraries;
160 }
161
162 static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFileData(
163 id<MTLDevice> device,
164 const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data,
165 const std::string& label) {
166 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
167 for (const auto& library_data : libraries_data) {
168 if (library_data == nullptr) {
169 FML_LOG(ERROR) << "Shader library data was null.";
170 return nil;
171 }
172
173 __block auto data = library_data;
174
175 auto dispatch_data =
176 ::dispatch_data_create(library_data->GetMapping(), // buffer
177 library_data->GetSize(), // size
178 dispatch_get_main_queue(), // queue
179 ^() {
180 // We just need a reference.
181 data.reset();
182 } // destructor
183 );
184 if (!dispatch_data) {
185 FML_LOG(ERROR) << "Could not wrap shader data in dispatch data.";
186 return nil;
187 }
188
189 NSError* shader_library_error = nil;
190 auto library = [device newLibraryWithData:dispatch_data
191 error:&shader_library_error];
192 if (!library) {
193 FML_LOG(ERROR) << "Could not create shader library: "
194 << shader_library_error.localizedDescription.UTF8String;
195 return nil;
196 }
197 if (!label.empty()) {
198 library.label = @(label.c_str());
199 }
200 [found_libraries addObject:library];
201 }
202 return found_libraries;
203 }
204
205 static id<MTLDevice> CreateMetalDevice() {
206 return ::MTLCreateSystemDefaultDevice();
207 }
208
209 static id<MTLCommandQueue> CreateMetalCommandQueue(id<MTLDevice> device) {
210 auto command_queue = device.newCommandQueue;
211 if (!command_queue) {
212 VALIDATION_LOG << "Could not set up the command queue.";
213 return nullptr;
214 }
215 command_queue.label = @"Impeller Command Queue";
216 return command_queue;
217 }
218
219 std::shared_ptr<ContextMTL> ContextMTL::Create(
220 const std::vector<std::string>& shader_library_paths,
221 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
222 auto device = CreateMetalDevice();
223 auto command_queue = CreateMetalCommandQueue(device);
224 if (!command_queue) {
225 return nullptr;
226 }
227 auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
228 device, command_queue,
229 MTLShaderLibraryFromFilePaths(device, shader_library_paths),
230 std::move(is_gpu_disabled_sync_switch)));
231 if (!context->IsValid()) {
232 FML_LOG(ERROR) << "Could not create Metal context.";
233 return nullptr;
234 }
235 return context;
236 }
237
238 std::shared_ptr<ContextMTL> ContextMTL::Create(
239 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
240 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
241 const std::string& library_label) {
242 auto device = CreateMetalDevice();
243 auto command_queue = CreateMetalCommandQueue(device);
244 if (!command_queue) {
245 return nullptr;
246 }
247 auto context = std::shared_ptr<ContextMTL>(
248 new ContextMTL(device, command_queue,
249 MTLShaderLibraryFromFileData(device, shader_libraries_data,
250 library_label),
251 std::move(is_gpu_disabled_sync_switch)));
252 if (!context->IsValid()) {
253 FML_LOG(ERROR) << "Could not create Metal context.";
254 return nullptr;
255 }
256 return context;
257 }
258
259 std::shared_ptr<ContextMTL> ContextMTL::Create(
260 id<MTLDevice> device,
261 id<MTLCommandQueue> command_queue,
262 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
263 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
264 const std::string& library_label) {
265 auto context = std::shared_ptr<ContextMTL>(
266 new ContextMTL(device, command_queue,
267 MTLShaderLibraryFromFileData(device, shader_libraries_data,
268 library_label),
269 std::move(is_gpu_disabled_sync_switch)));
270 if (!context->IsValid()) {
271 FML_LOG(ERROR) << "Could not create Metal context.";
272 return nullptr;
273 }
274 return context;
275 }
276
277 ContextMTL::~ContextMTL() {
278 is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
279 }
280
281 Context::BackendType ContextMTL::GetBackendType() const {
282 return Context::BackendType::kMetal;
283 }
284
285 // |Context|
286 std::string ContextMTL::DescribeGpuModel() const {
287 return std::string([[device_ name] UTF8String]);
288 }
289
290 // |Context|
291 bool ContextMTL::IsValid() const {
292 return is_valid_;
293 }
294
295 // |Context|
296 std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary() const {
297 return shader_library_;
298 }
299
300 // |Context|
301 std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary() const {
302 return pipeline_library_;
303 }
304
305 // |Context|
306 std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary() const {
307 return sampler_library_;
308 }
309
310 // |Context|
311 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBuffer() const {
312 return CreateCommandBufferInQueue(command_queue_);
313 }
314
315 // |Context|
316 void ContextMTL::Shutdown() {}
317
318 #ifdef IMPELLER_DEBUG
319 std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer() const {
320 return gpu_tracer_;
321 }
322 #endif // IMPELLER_DEBUG
323
324 std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
325 const {
326 return is_gpu_disabled_sync_switch_;
327 }
328
329 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
330 id<MTLCommandQueue> queue) const {
331 if (!IsValid()) {
332 return nullptr;
333 }
334
335 auto buffer = std::shared_ptr<CommandBufferMTL>(
336 new CommandBufferMTL(weak_from_this(), queue));
337 if (!buffer->IsValid()) {
338 return nullptr;
339 }
340 return buffer;
341 }
342
343 std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator() const {
344 return resource_allocator_;
345 }
346
347 id<MTLDevice> ContextMTL::GetMTLDevice() const {
348 return device_;
349 }
350
351 const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities() const {
352 return device_capabilities_;
353 }
354
355 void ContextMTL::SetCapabilities(
356 const std::shared_ptr<const Capabilities>& capabilities) {
357 device_capabilities_ = capabilities;
358 }
359
360 // |Context|
361 bool ContextMTL::UpdateOffscreenLayerPixelFormat(PixelFormat format) {
362 device_capabilities_ = InferMetalCapabilities(device_, format);
363 return true;
364 }
365
366 id<MTLCommandBuffer> ContextMTL::CreateMTLCommandBuffer(
367 const std::string& label) const {
368 auto buffer = [command_queue_ commandBuffer];
369 if (!label.empty()) {
370 [buffer setLabel:@(label.data())];
371 }
372 return buffer;
373 }
374
375 void ContextMTL::StoreTaskForGPU(const std::function<void()>& task) {
376 tasks_awaiting_gpu_.emplace_back(task);
377 while (tasks_awaiting_gpu_.size() > kMaxTasksAwaitingGPU) {
378 tasks_awaiting_gpu_.front()();
379 tasks_awaiting_gpu_.pop_front();
380 }
381 }
382
383 void ContextMTL::FlushTasksAwaitingGPU() {
384 for (const auto& task : tasks_awaiting_gpu_) {
385 task();
386 }
387 tasks_awaiting_gpu_.clear();
388 }
389
390 ContextMTL::SyncSwitchObserver::SyncSwitchObserver(ContextMTL& parent)
391 : parent_(parent) {}
392
393 void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(bool new_is_disabled) {
394 if (!new_is_disabled) {
395 parent_.FlushTasksAwaitingGPU();
396 }
397 }
398
399 // |Context|
400 std::shared_ptr<CommandQueue> ContextMTL::GetCommandQueue() const {
401 return command_queue_ip_;
402 }
403
404 } // namespace impeller
impeller::PixelFormat::kS8UInt
@ kS8UInt
impeller::CapabilitiesBuilder::Build
std::unique_ptr< Capabilities > Build()
Definition: capabilities.cc:226
impeller::Context::BackendType
BackendType
Definition: context.h:48
impeller::DeviceSupportsComputeSubgroups
static bool DeviceSupportsComputeSubgroups(id< MTLDevice > device)
Definition: context_mtl.mm:42
impeller::PixelFormat::kA8UNormInt
@ kA8UNormInt
context_mtl.h
impeller::CreateCommandBuffer
static id< MTLCommandBuffer > CreateCommandBuffer(id< MTLCommandQueue > queue)
Definition: command_buffer_mtl.mm:117
formats.h
impeller::CapabilitiesBuilder
Definition: capabilities.h:122
impeller::MTLShaderLibraryFromFileData
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:162
sampler_library_mtl.h
impeller::PixelFormat::kD32FloatS8UInt
@ kD32FloatS8UInt
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:100
impeller::CreateMetalCommandQueue
static id< MTLCommandQueue > CreateMetalCommandQueue(id< MTLDevice > device)
Definition: context_mtl.mm:209
impeller::CapabilitiesBuilder::SetSupportsSSBO
CapabilitiesBuilder & SetSupportsSSBO(bool value)
Definition: capabilities.cc:150
impeller::CapabilitiesBuilder::SetSupportsCompute
CapabilitiesBuilder & SetSupportsCompute(bool value)
Definition: capabilities.cc:173
impeller::CapabilitiesBuilder::SetDefaultDepthStencilFormat
CapabilitiesBuilder & SetDefaultDepthStencilFormat(PixelFormat value)
Definition: capabilities.cc:196
impeller::CapabilitiesBuilder::SetDefaultGlyphAtlasFormat
CapabilitiesBuilder & SetDefaultGlyphAtlasFormat(PixelFormat value)
Definition: capabilities.cc:220
impeller::DeviceSupportsFramebufferFetch
static bool DeviceSupportsFramebufferFetch(id< MTLDevice > device)
Definition: context_mtl.mm:22
impeller::CapabilitiesBuilder::SetSupportsFramebufferFetch
CapabilitiesBuilder & SetSupportsFramebufferFetch(bool value)
Definition: capabilities.cc:167
impeller::CapabilitiesBuilder::SetSupportsComputeSubgroups
CapabilitiesBuilder & SetSupportsComputeSubgroups(bool value)
Definition: capabilities.cc:178
capabilities.h
impeller::CapabilitiesBuilder::SetSupportsTextureToTextureBlits
CapabilitiesBuilder & SetSupportsTextureToTextureBlits(bool value)
Definition: capabilities.cc:161
impeller::CreateMetalDevice
static id< MTLDevice > CreateMetalDevice()
Definition: context_mtl.mm:205
impeller::InferMetalCapabilities
static std::unique_ptr< Capabilities > InferMetalCapabilities(id< MTLDevice > device, PixelFormat color_format)
Definition: context_mtl.mm:53
impeller::CapabilitiesBuilder::SetDefaultColorFormat
CapabilitiesBuilder & SetDefaultColorFormat(PixelFormat value)
Definition: capabilities.cc:184
sampler_descriptor.h
impeller::CapabilitiesBuilder::SetDefaultStencilFormat
CapabilitiesBuilder & SetDefaultStencilFormat(PixelFormat value)
Definition: capabilities.cc:190
impeller::CapabilitiesBuilder::SetSupportsBufferToTextureBlits
CapabilitiesBuilder & SetSupportsBufferToTextureBlits(bool value)
Definition: capabilities.cc:155
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::CapabilitiesBuilder::SetSupportsDecalSamplerAddressMode
CapabilitiesBuilder & SetSupportsDecalSamplerAddressMode(bool value)
Definition: capabilities.cc:208
std
Definition: comparable.h:95
impeller::MTLShaderLibraryFromFilePaths
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFilePaths(id< MTLDevice > device, const std::vector< std::string > &libraries_paths)
Definition: context_mtl.mm:139
impeller::ContextMTL::GetCommandQueue
std::shared_ptr< CommandQueue > GetCommandQueue() const override
Return the graphics queue for submitting command buffers.
Definition: context_mtl.mm:400
gpu_tracer_mtl.h
impeller::CapabilitiesBuilder::SetSupportsDeviceTransientTextures
CapabilitiesBuilder & SetSupportsDeviceTransientTextures(bool value)
Definition: capabilities.cc:214
impeller::ContextMTL
Definition: context_mtl.h:35
impeller::PixelFormat::kB8G8R8A8UNormInt
@ kB8G8R8A8UNormInt
impeller
Definition: aiks_blur_unittests.cc:20
impeller::CapabilitiesBuilder::SetSupportsReadFromResolve
CapabilitiesBuilder & SetSupportsReadFromResolve(bool value)
Definition: capabilities.cc:202
impeller::CapabilitiesBuilder::SetSupportsOffscreenMSAA
CapabilitiesBuilder & SetSupportsOffscreenMSAA(bool value)
Definition: capabilities.cc:145