| 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 .SetSupportsTextureToTextureBlits(true)
60 .SetSupportsDecalSamplerAddressMode(true)
61 .SetSupportsFramebufferFetch(DeviceSupportsFramebufferFetch(device))
62 .SetDefaultColorFormat(color_format)
63 .SetDefaultStencilFormat(PixelFormat::kS8UInt)
64 .SetDefaultDepthStencilFormat(PixelFormat::kD32FloatS8UInt)
65 .SetSupportsCompute(true)
66 .SetSupportsComputeSubgroups(DeviceSupportsComputeSubgroups(device))
67 .SetSupportsReadFromResolve(true)
68 .SetSupportsDeviceTransientTextures(true)
69 .SetDefaultGlyphAtlasFormat(PixelFormat::kA8UNormInt)
70 .Build();
71 }
72
73 ContextMTL::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()
133 : PixelFormat::kB8G8R8A8UNormInt);
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
141 static 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
164 static 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
207 static id<MTLDevice> CreateMetalDevice() {
208 return ::MTLCreateSystemDefaultDevice();
209 }
210
211 static 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
221 std::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
240 std::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
262 std::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
280 ContextMTL::~ContextMTL() {
281 is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
282 }
283
284 Context::BackendType ContextMTL::GetBackendType() const {
285 return Context::BackendType::kMetal;
286 }
287
288 // |Context|
289 std::string ContextMTL::DescribeGpuModel() const {
290 return std::string([[device_ name] UTF8String]);
291 }
292
293 // |Context|
294 bool ContextMTL::IsValid() const {
295 return is_valid_;
296 }
297
298 // |Context|
299 std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary() const {
300 return shader_library_;
301 }
302
303 // |Context|
304 std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary() const {
305 return pipeline_library_;
306 }
307
308 // |Context|
309 std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary() const {
310 return sampler_library_;
311 }
312
313 // |Context|
314 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBuffer() const {
315 return CreateCommandBufferInQueue(command_queue_);
316 }
317
318 // |Context|
319 void ContextMTL::Shutdown() {}
320
321 #ifdef IMPELLER_DEBUG
322 std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer() const {
323 return gpu_tracer_;
324 }
325 #endif // IMPELLER_DEBUG
326
327 std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
328 const {
329 return is_gpu_disabled_sync_switch_;
330 }
331
332 std::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
346 std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator() const {
347 return resource_allocator_;
348 }
349
350 id<MTLDevice> ContextMTL::GetMTLDevice() const {
351 return device_;
352 }
353
354 const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities() const {
355 return device_capabilities_;
356 }
357
358 void ContextMTL::SetCapabilities(
359 const std::shared_ptr<const Capabilities>& capabilities) {
360 device_capabilities_ = capabilities;
361 }
362
363 // |Context|
364 bool ContextMTL::UpdateOffscreenLayerPixelFormat(PixelFormat format) {
365 device_capabilities_ = InferMetalCapabilities(device_, format);
366 return true;
367 }
368
369 id<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
378 void 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
386 void ContextMTL::FlushTasksAwaitingGPU() {
387 for (const auto& task : tasks_awaiting_gpu_) {
388 task();
389 }
390 tasks_awaiting_gpu_.clear();
391 }
392
393 ContextMTL::SyncSwitchObserver::SyncSwitchObserver(ContextMTL& parent)
394 : parent_(parent) {}
395
396 void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(bool new_is_disabled) {
397 if (!new_is_disabled) {
398 parent_.FlushTasksAwaitingGPU();
399 }
400 }
401
402 // |Context|
403 std::shared_ptr<CommandQueue> ContextMTL::GetCommandQueue() const {
404 return command_queue_ip_;
405 }
406
407 } // namespace impeller
impeller::PixelFormat::kS8UInt
@ kS8UInt
impeller::CapabilitiesBuilder::Build
std::unique_ptr< Capabilities > Build()
Definition: capabilities.cc:212
impeller::Context::BackendType
BackendType
Definition: context.h:47
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
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
formats.h
impeller::CapabilitiesBuilder
Definition: capabilities.h:116
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:164
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:99
impeller::CreateMetalCommandQueue
static id< MTLCommandQueue > CreateMetalCommandQueue(id< MTLDevice > device)
Definition: context_mtl.mm:211
impeller::CapabilitiesBuilder::SetSupportsSSBO
CapabilitiesBuilder & SetSupportsSSBO(bool value)
Definition: capabilities.cc:142
impeller::CapabilitiesBuilder::SetSupportsCompute
CapabilitiesBuilder & SetSupportsCompute(bool value)
Definition: capabilities.cc:159
impeller::CapabilitiesBuilder::SetDefaultDepthStencilFormat
CapabilitiesBuilder & SetDefaultDepthStencilFormat(PixelFormat value)
Definition: capabilities.cc:182
impeller::CapabilitiesBuilder::SetDefaultGlyphAtlasFormat
CapabilitiesBuilder & SetDefaultGlyphAtlasFormat(PixelFormat value)
Definition: capabilities.cc:206
impeller::DeviceSupportsFramebufferFetch
static bool DeviceSupportsFramebufferFetch(id< MTLDevice > device)
Definition: context_mtl.mm:22
impeller::CapabilitiesBuilder::SetSupportsFramebufferFetch
CapabilitiesBuilder & SetSupportsFramebufferFetch(bool value)
Definition: capabilities.cc:153
impeller::CapabilitiesBuilder::SetSupportsComputeSubgroups
CapabilitiesBuilder & SetSupportsComputeSubgroups(bool value)
Definition: capabilities.cc:164
capabilities.h
impeller::CapabilitiesBuilder::SetSupportsTextureToTextureBlits
CapabilitiesBuilder & SetSupportsTextureToTextureBlits(bool value)
Definition: capabilities.cc:147
impeller::CreateMetalDevice
static id< MTLDevice > CreateMetalDevice()
Definition: context_mtl.mm:207
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:170
sampler_descriptor.h
impeller::CapabilitiesBuilder::SetDefaultStencilFormat
CapabilitiesBuilder & SetDefaultStencilFormat(PixelFormat value)
Definition: capabilities.cc:176
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
impeller::CapabilitiesBuilder::SetSupportsDecalSamplerAddressMode
CapabilitiesBuilder & SetSupportsDecalSamplerAddressMode(bool value)
Definition: capabilities.cc:194
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:141
impeller::ContextMTL::GetCommandQueue
std::shared_ptr< CommandQueue > GetCommandQueue() const override
Return the graphics queue for submitting command buffers.
Definition: context_mtl.mm:403
gpu_tracer_mtl.h
impeller::CapabilitiesBuilder::SetSupportsDeviceTransientTextures
CapabilitiesBuilder & SetSupportsDeviceTransientTextures(bool value)
Definition: capabilities.cc:200
impeller::ContextMTL
Definition: context_mtl.h:34
impeller::PixelFormat::kB8G8R8A8UNormInt
@ kB8G8R8A8UNormInt
impeller
Definition: aiks_blend_unittests.cc:18
impeller::CapabilitiesBuilder::SetSupportsReadFromResolve
CapabilitiesBuilder & SetSupportsReadFromResolve(bool value)
Definition: capabilities.cc:188
impeller::CapabilitiesBuilder::SetSupportsOffscreenMSAA
CapabilitiesBuilder & SetSupportsOffscreenMSAA(bool value)
Definition: capabilities.cc:137