| 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 <Foundation/Foundation.h>
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/sampler_descriptor.h"
15 #include "impeller/renderer/backend/metal/sampler_library_mtl.h"
16 #include "impeller/renderer/capabilities.h"
17
18 namespace impeller {
19
20 static bool DeviceSupportsFramebufferFetch(id<MTLDevice> device) {
21 // The iOS simulator lies about supporting framebuffer fetch.
22 #if FML_OS_IOS_SIMULATOR
23 return false;
24 #else // FML_OS_IOS_SIMULATOR
25
26 if (@available(macOS 10.15, iOS 13, tvOS 13, *)) {
27 return [device supportsFamily:MTLGPUFamilyApple2];
28 }
29 // According to
30 // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf , Apple2
31 // corresponds to iOS GPU family 2, which supports A8 devices.
32 #if FML_OS_IOS
33 return [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1];
34 #else
35 return false;
36 #endif // FML_OS_IOS
37 #endif // FML_OS_IOS_SIMULATOR
38 }
39
40 static bool DeviceSupportsComputeSubgroups(id<MTLDevice> device) {
41 bool supports_subgroups = false;
42 // Refer to the "SIMD-scoped reduction operations" feature in the table
43 // below: https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
44 if (@available(ios 13.0, tvos 13.0, macos 10.15, *)) {
45 supports_subgroups = [device supportsFamily:MTLGPUFamilyApple7] ||
46 [device supportsFamily:MTLGPUFamilyMac2];
47 }
48 return supports_subgroups;
49 }
50
51 static std::unique_ptr<Capabilities> InferMetalCapabilities(
52 id<MTLDevice> device,
53 PixelFormat color_format) {
54 return CapabilitiesBuilder()
55 .SetSupportsOffscreenMSAA(true)
56 .SetSupportsSSBO(true)
57 .SetSupportsBufferToTextureBlits(true)
58 .SetSupportsTextureToTextureBlits(true)
59 .SetSupportsDecalSamplerAddressMode(true)
60 .SetSupportsFramebufferFetch(DeviceSupportsFramebufferFetch(device))
61 .SetDefaultColorFormat(color_format)
62 .SetDefaultStencilFormat(PixelFormat::kS8UInt)
63 .SetDefaultDepthStencilFormat(PixelFormat::kD32FloatS8UInt)
64 .SetSupportsCompute(true)
65 .SetSupportsComputeSubgroups(DeviceSupportsComputeSubgroups(device))
66 .SetSupportsReadFromResolve(true)
67 .SetSupportsReadFromOnscreenTexture(true)
68 .SetSupportsDeviceTransientTextures(true)
69 .Build();
70 }
71
72 ContextMTL::ContextMTL(
73 id<MTLDevice> device,
74 id<MTLCommandQueue> command_queue,
75 NSArray<id<MTLLibrary>>* shader_libraries,
76 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch)
77 : device_(device),
78 command_queue_(command_queue),
79 is_gpu_disabled_sync_switch_(std::move(is_gpu_disabled_sync_switch)) {
80 // Validate device.
81 if (!device_) {
82 VALIDATION_LOG << "Could not set up valid Metal device.";
83 return;
84 }
85
86 // Worker task runner.
87 {
88 raster_message_loop_ = fml::ConcurrentMessageLoop::Create(
89 std::min(4u, std::thread::hardware_concurrency()));
90 raster_message_loop_->PostTaskToAllWorkers([]() {
91 // See https://github.com/flutter/flutter/issues/65752
92 // Intentionally opt out of QoS for raster task workloads.
93 [[NSThread currentThread] setThreadPriority:1.0];
94 sched_param param;
95 int policy;
96 pthread_t thread = pthread_self();
97 if (!pthread_getschedparam(thread, &policy, &param)) {
98 param.sched_priority = 50;
99 pthread_setschedparam(thread, policy, &param);
100 }
101 });
102 }
103
104 // Setup the shader library.
105 {
106 if (shader_libraries == nil) {
107 VALIDATION_LOG << "Shader libraries were null.";
108 return;
109 }
110
111 // std::make_shared disallowed because of private friend ctor.
112 auto library = std::shared_ptr<ShaderLibraryMTL>(
113 new ShaderLibraryMTL(shader_libraries));
114 if (!library->IsValid()) {
115 VALIDATION_LOG << "Could not create valid Metal shader library.";
116 return;
117 }
118 shader_library_ = std::move(library);
119 }
120
121 // Setup the pipeline library.
122 {
123 pipeline_library_ =
124 std::shared_ptr<PipelineLibraryMTL>(new PipelineLibraryMTL(device_));
125 }
126
127 // Setup the sampler library.
128 {
129 sampler_library_ =
130 std::shared_ptr<SamplerLibraryMTL>(new SamplerLibraryMTL(device_));
131 }
132
133 // Setup the resource allocator.
134 {
135 resource_allocator_ = std::shared_ptr<AllocatorMTL>(
136 new AllocatorMTL(device_, "Impeller Permanents Allocator"));
137 if (!resource_allocator_) {
138 VALIDATION_LOG << "Could not set up the resource allocator.";
139 return;
140 }
141 }
142
143 device_capabilities_ =
144 InferMetalCapabilities(device_, PixelFormat::kB8G8R8A8UNormInt);
145
146 is_valid_ = true;
147 }
148
149 static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFilePaths(
150 id<MTLDevice> device,
151 const std::vector<std::string>& libraries_paths) {
152 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
153 for (const auto& library_path : libraries_paths) {
154 if (!fml::IsFile(library_path)) {
155 VALIDATION_LOG << "Shader library does not exist at path '"
156 << library_path << "'";
157 return nil;
158 }
159 NSError* shader_library_error = nil;
160 auto library = [device newLibraryWithFile:@(library_path.c_str())
161 error:&shader_library_error];
162 if (!library) {
163 FML_LOG(ERROR) << "Could not create shader library: "
164 << shader_library_error.localizedDescription.UTF8String;
165 return nil;
166 }
167 [found_libraries addObject:library];
168 }
169 return found_libraries;
170 }
171
172 static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFileData(
173 id<MTLDevice> device,
174 const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data,
175 const std::string& label) {
176 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
177 for (const auto& library_data : libraries_data) {
178 if (library_data == nullptr) {
179 FML_LOG(ERROR) << "Shader library data was null.";
180 return nil;
181 }
182
183 __block auto data = library_data;
184
185 auto dispatch_data =
186 ::dispatch_data_create(library_data->GetMapping(), // buffer
187 library_data->GetSize(), // size
188 dispatch_get_main_queue(), // queue
189 ^() {
190 // We just need a reference.
191 data.reset();
192 } // destructor
193 );
194 if (!dispatch_data) {
195 FML_LOG(ERROR) << "Could not wrap shader data in dispatch data.";
196 return nil;
197 }
198
199 NSError* shader_library_error = nil;
200 auto library = [device newLibraryWithData:dispatch_data
201 error:&shader_library_error];
202 if (!library) {
203 FML_LOG(ERROR) << "Could not create shader library: "
204 << shader_library_error.localizedDescription.UTF8String;
205 return nil;
206 }
207 if (!label.empty()) {
208 library.label = @(label.c_str());
209 }
210 [found_libraries addObject:library];
211 }
212 return found_libraries;
213 }
214
215 static id<MTLDevice> CreateMetalDevice() {
216 return ::MTLCreateSystemDefaultDevice();
217 }
218
219 static id<MTLCommandQueue> CreateMetalCommandQueue(id<MTLDevice> device) {
220 auto command_queue = device.newCommandQueue;
221 if (!command_queue) {
222 VALIDATION_LOG << "Could not set up the command queue.";
223 return nullptr;
224 }
225 command_queue.label = @"Impeller Command Queue";
226 return command_queue;
227 }
228
229 std::shared_ptr<ContextMTL> ContextMTL::Create(
230 const std::vector<std::string>& shader_library_paths,
231 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
232 auto device = CreateMetalDevice();
233 auto command_queue = CreateMetalCommandQueue(device);
234 if (!command_queue) {
235 return nullptr;
236 }
237 auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
238 device, command_queue,
239 MTLShaderLibraryFromFilePaths(device, shader_library_paths),
240 std::move(is_gpu_disabled_sync_switch)));
241 if (!context->IsValid()) {
242 FML_LOG(ERROR) << "Could not create Metal context.";
243 return nullptr;
244 }
245 return context;
246 }
247
248 std::shared_ptr<ContextMTL> ContextMTL::Create(
249 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
250 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
251 const std::string& library_label) {
252 auto device = CreateMetalDevice();
253 auto command_queue = CreateMetalCommandQueue(device);
254 if (!command_queue) {
255 return nullptr;
256 }
257 auto context = std::shared_ptr<ContextMTL>(
258 new ContextMTL(device, command_queue,
259 MTLShaderLibraryFromFileData(device, shader_libraries_data,
260 library_label),
261 std::move(is_gpu_disabled_sync_switch)));
262 if (!context->IsValid()) {
263 FML_LOG(ERROR) << "Could not create Metal context.";
264 return nullptr;
265 }
266 return context;
267 }
268
269 std::shared_ptr<ContextMTL> ContextMTL::Create(
270 id<MTLDevice> device,
271 id<MTLCommandQueue> command_queue,
272 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
273 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
274 const std::string& library_label) {
275 auto context = std::shared_ptr<ContextMTL>(
276 new ContextMTL(device, command_queue,
277 MTLShaderLibraryFromFileData(device, shader_libraries_data,
278 library_label),
279 std::move(is_gpu_disabled_sync_switch)));
280 if (!context->IsValid()) {
281 FML_LOG(ERROR) << "Could not create Metal context.";
282 return nullptr;
283 }
284 return context;
285 }
286
287 ContextMTL::~ContextMTL() = default;
288
289 Context::BackendType ContextMTL::GetBackendType() const {
290 return Context::BackendType::kMetal;
291 }
292
293 // |Context|
294 std::string ContextMTL::DescribeGpuModel() const {
295 return std::string([[device_ name] UTF8String]);
296 }
297
298 // |Context|
299 bool ContextMTL::IsValid() const {
300 return is_valid_;
301 }
302
303 // |Context|
304 std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary() const {
305 return shader_library_;
306 }
307
308 // |Context|
309 std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary() const {
310 return pipeline_library_;
311 }
312
313 // |Context|
314 std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary() const {
315 return sampler_library_;
316 }
317
318 // |Context|
319 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBuffer() const {
320 return CreateCommandBufferInQueue(command_queue_);
321 }
322
323 // |Context|
324 void ContextMTL::Shutdown() {
325 raster_message_loop_.reset();
326 }
327
328 const std::shared_ptr<fml::ConcurrentTaskRunner>
329 ContextMTL::GetWorkerTaskRunner() const {
330 return raster_message_loop_->GetTaskRunner();
331 }
332
333 std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
334 const {
335 return is_gpu_disabled_sync_switch_;
336 }
337
338 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
339 id<MTLCommandQueue> queue) const {
340 if (!IsValid()) {
341 return nullptr;
342 }
343
344 auto buffer = std::shared_ptr<CommandBufferMTL>(
345 new CommandBufferMTL(weak_from_this(), queue));
346 if (!buffer->IsValid()) {
347 return nullptr;
348 }
349 return buffer;
350 }
351
352 std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator() const {
353 return resource_allocator_;
354 }
355
356 id<MTLDevice> ContextMTL::GetMTLDevice() const {
357 return device_;
358 }
359
360 const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities() const {
361 return device_capabilities_;
362 }
363
364 // |Context|
365 bool ContextMTL::UpdateOffscreenLayerPixelFormat(PixelFormat format) {
366 device_capabilities_ = InferMetalCapabilities(device_, format);
367 return true;
368 }
369
370 id<MTLCommandBuffer> ContextMTL::CreateMTLCommandBuffer(
371 const std::string& label) const {
372 auto buffer = [command_queue_ commandBuffer];
373 if (!label.empty()) {
374 [buffer setLabel:@(label.data())];
375 }
376 return buffer;
377 }
378
379 } // namespace impeller
impeller::CapabilitiesBuilder::Build
std::unique_ptr< Capabilities > Build()
Definition: capabilities.cc:220
impeller::Context::BackendType
BackendType
Definition: context.h:49
impeller::DeviceSupportsComputeSubgroups
static bool DeviceSupportsComputeSubgroups(id< MTLDevice > device)
Definition: context_mtl.mm:40
context_mtl.h
impeller::CreateCommandBuffer
static id< MTLCommandBuffer > CreateCommandBuffer(id< MTLCommandQueue > queue)
Definition: command_buffer_mtl.mm:118
impeller::CapabilitiesBuilder
Definition: capabilities.h:112
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:172
sampler_library_mtl.h
impeller::CreateMetalCommandQueue
static id< MTLCommandQueue > CreateMetalCommandQueue(id< MTLDevice > device)
Definition: context_mtl.mm:219
impeller::CapabilitiesBuilder::SetSupportsSSBO
CapabilitiesBuilder & SetSupportsSSBO(bool value)
Definition: capabilities.cc:144
impeller::CapabilitiesBuilder::SetSupportsCompute
CapabilitiesBuilder & SetSupportsCompute(bool value)
Definition: capabilities.cc:167
impeller::CapabilitiesBuilder::SetDefaultDepthStencilFormat
CapabilitiesBuilder & SetDefaultDepthStencilFormat(PixelFormat value)
Definition: capabilities.cc:202
impeller::DeviceSupportsFramebufferFetch
static bool DeviceSupportsFramebufferFetch(id< MTLDevice > device)
Definition: context_mtl.mm:20
impeller::CapabilitiesBuilder::SetSupportsFramebufferFetch
CapabilitiesBuilder & SetSupportsFramebufferFetch(bool value)
Definition: capabilities.cc:161
impeller::PixelFormat::kD32FloatS8UInt
@ kD32FloatS8UInt
impeller::CapabilitiesBuilder::SetSupportsComputeSubgroups
CapabilitiesBuilder & SetSupportsComputeSubgroups(bool value)
Definition: capabilities.cc:172
capabilities.h
impeller::CapabilitiesBuilder::SetSupportsTextureToTextureBlits
CapabilitiesBuilder & SetSupportsTextureToTextureBlits(bool value)
Definition: capabilities.cc:155
impeller::CreateMetalDevice
static id< MTLDevice > CreateMetalDevice()
Definition: context_mtl.mm:215
impeller::PixelFormat::kB8G8R8A8UNormInt
@ kB8G8R8A8UNormInt
impeller::InferMetalCapabilities
static std::unique_ptr< Capabilities > InferMetalCapabilities(id< MTLDevice > device, PixelFormat color_format)
Definition: context_mtl.mm:51
impeller::CapabilitiesBuilder::SetDefaultColorFormat
CapabilitiesBuilder & SetDefaultColorFormat(PixelFormat value)
Definition: capabilities.cc:190
sampler_descriptor.h
impeller::CapabilitiesBuilder::SetDefaultStencilFormat
CapabilitiesBuilder & SetDefaultStencilFormat(PixelFormat value)
Definition: capabilities.cc:196
impeller::CapabilitiesBuilder::SetSupportsBufferToTextureBlits
CapabilitiesBuilder & SetSupportsBufferToTextureBlits(bool value)
Definition: capabilities.cc:149
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:60
impeller::CapabilitiesBuilder::SetSupportsDecalSamplerAddressMode
CapabilitiesBuilder & SetSupportsDecalSamplerAddressMode(bool value)
Definition: capabilities.cc:208
std
Definition: comparable.h:98
impeller::PixelFormat::kS8UInt
@ kS8UInt
impeller::MTLShaderLibraryFromFilePaths
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFilePaths(id< MTLDevice > device, const std::vector< std::string > &libraries_paths)
Definition: context_mtl.mm:149
impeller::CapabilitiesBuilder::SetSupportsDeviceTransientTextures
CapabilitiesBuilder & SetSupportsDeviceTransientTextures(bool value)
Definition: capabilities.cc:214
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:94
impeller::ContextMTL
Definition: context_mtl.h:32
impeller::CapabilitiesBuilder::SetSupportsReadFromOnscreenTexture
CapabilitiesBuilder & SetSupportsReadFromOnscreenTexture(bool value)
Definition: capabilities.cc:178
impeller
Definition: aiks_context.cc:10
impeller::CapabilitiesBuilder::SetSupportsReadFromResolve
CapabilitiesBuilder & SetSupportsReadFromResolve(bool value)
Definition: capabilities.cc:184
impeller::CapabilitiesBuilder::SetSupportsOffscreenMSAA
CapabilitiesBuilder & SetSupportsOffscreenMSAA(bool value)
Definition: capabilities.cc:139