Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
73
74ContextMTL::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 std::optional<PixelFormat> pixel_format_override)
80 : device_(device),
81 command_queue_(command_queue),
82 is_gpu_disabled_sync_switch_(std::move(is_gpu_disabled_sync_switch)) {
83 // Validate device.
84 if (!device_) {
85 VALIDATION_LOG << "Could not set up valid Metal device.";
86 return;
87 }
88
89 sync_switch_observer_.reset(new SyncSwitchObserver(*this));
90 is_gpu_disabled_sync_switch_->AddObserver(sync_switch_observer_.get());
91
92 // Setup the shader library.
93 {
94 if (shader_libraries == nil) {
95 VALIDATION_LOG << "Shader libraries were null.";
96 return;
97 }
98
99 // std::make_shared disallowed because of private friend ctor.
100 auto library = std::shared_ptr<ShaderLibraryMTL>(
101 new ShaderLibraryMTL(shader_libraries));
102 if (!library->IsValid()) {
103 VALIDATION_LOG << "Could not create valid Metal shader library.";
104 return;
105 }
106 shader_library_ = std::move(library);
107 }
108
109 // Setup the pipeline library.
110 {
111 pipeline_library_ =
112 std::shared_ptr<PipelineLibraryMTL>(new PipelineLibraryMTL(device_));
113 }
114
115 // Setup the sampler library.
116 {
117 sampler_library_ =
118 std::shared_ptr<SamplerLibraryMTL>(new SamplerLibraryMTL(device_));
119 }
120
121 // Setup the resource allocator.
122 {
123 resource_allocator_ = std::shared_ptr<AllocatorMTL>(
124 new AllocatorMTL(device_, "Impeller Permanents Allocator"));
125 if (!resource_allocator_) {
126 VALIDATION_LOG << "Could not set up the resource allocator.";
127 return;
128 }
129 }
130
131 device_capabilities_ =
132 InferMetalCapabilities(device_, pixel_format_override.has_value()
133 ? pixel_format_override.value()
134 : PixelFormat::kB8G8R8A8UNormInt);
135 command_queue_ip_ = std::make_shared<CommandQueue>();
136#ifdef IMPELLER_DEBUG
137 gpu_tracer_ = std::make_shared<GPUTracerMTL>();
138#endif // IMPELLER_DEBUG
139 is_valid_ = true;
140}
141
142static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFilePaths(
143 id<MTLDevice> device,
144 const std::vector<std::string>& libraries_paths) {
145 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
146 for (const auto& library_path : libraries_paths) {
147 if (!fml::IsFile(library_path)) {
148 VALIDATION_LOG << "Shader library does not exist at path '"
149 << library_path << "'";
150 return nil;
151 }
152 NSError* shader_library_error = nil;
153 auto library = [device newLibraryWithFile:@(library_path.c_str())
154 error:&shader_library_error];
155 if (!library) {
156 FML_LOG(ERROR) << "Could not create shader library: "
157 << shader_library_error.localizedDescription.UTF8String;
158 return nil;
159 }
160 [found_libraries addObject:library];
161 }
162 return found_libraries;
163}
164
165static NSArray<id<MTLLibrary>>* MTLShaderLibraryFromFileData(
166 id<MTLDevice> device,
167 const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data,
168 const std::string& label) {
169 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
170 for (const auto& library_data : libraries_data) {
171 if (library_data == nullptr) {
172 FML_LOG(ERROR) << "Shader library data was null.";
173 return nil;
174 }
175
176 __block auto data = library_data;
177
178 auto dispatch_data =
179 ::dispatch_data_create(library_data->GetMapping(), // buffer
180 library_data->GetSize(), // size
181 dispatch_get_main_queue(), // queue
182 ^() {
183 // We just need a reference.
184 data.reset();
185 } // destructor
186 );
187 if (!dispatch_data) {
188 FML_LOG(ERROR) << "Could not wrap shader data in dispatch data.";
189 return nil;
190 }
191
192 NSError* shader_library_error = nil;
193 auto library = [device newLibraryWithData:dispatch_data
194 error:&shader_library_error];
195 if (!library) {
196 FML_LOG(ERROR) << "Could not create shader library: "
197 << shader_library_error.localizedDescription.UTF8String;
198 return nil;
199 }
200 if (!label.empty()) {
201 library.label = @(label.c_str());
202 }
203 [found_libraries addObject:library];
204 }
205 return found_libraries;
206}
207
208static id<MTLDevice> CreateMetalDevice() {
209 return ::MTLCreateSystemDefaultDevice();
210}
211
212static id<MTLCommandQueue> CreateMetalCommandQueue(id<MTLDevice> device) {
213 auto command_queue = device.newCommandQueue;
214 if (!command_queue) {
215 VALIDATION_LOG << "Could not set up the command queue.";
216 return nullptr;
217 }
218 command_queue.label = @"Impeller Command Queue";
219 return command_queue;
220}
221
222std::shared_ptr<ContextMTL> ContextMTL::Create(
223 const std::vector<std::string>& shader_library_paths,
224 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
225 auto device = CreateMetalDevice();
226 auto command_queue = CreateMetalCommandQueue(device);
227 if (!command_queue) {
228 return nullptr;
229 }
230 auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
231 device, command_queue,
232 MTLShaderLibraryFromFilePaths(device, shader_library_paths),
233 std::move(is_gpu_disabled_sync_switch)));
234 if (!context->IsValid()) {
235 FML_LOG(ERROR) << "Could not create Metal context.";
236 return nullptr;
237 }
238 return context;
239}
240
241std::shared_ptr<ContextMTL> ContextMTL::Create(
242 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
243 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
244 const std::string& library_label,
245 std::optional<PixelFormat> pixel_format_override) {
246 auto device = CreateMetalDevice();
247 auto command_queue = CreateMetalCommandQueue(device);
248 if (!command_queue) {
249 return nullptr;
250 }
251 auto context = std::shared_ptr<ContextMTL>(new ContextMTL(
252 device, command_queue,
253 MTLShaderLibraryFromFileData(device, shader_libraries_data,
254 library_label),
255 std::move(is_gpu_disabled_sync_switch), pixel_format_override));
256 if (!context->IsValid()) {
257 FML_LOG(ERROR) << "Could not create Metal context.";
258 return nullptr;
259 }
260 return context;
261}
262
263std::shared_ptr<ContextMTL> ContextMTL::Create(
264 id<MTLDevice> device,
265 id<MTLCommandQueue> command_queue,
266 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
267 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
268 const std::string& library_label) {
269 auto context = std::shared_ptr<ContextMTL>(
270 new ContextMTL(device, command_queue,
271 MTLShaderLibraryFromFileData(device, shader_libraries_data,
272 library_label),
273 std::move(is_gpu_disabled_sync_switch)));
274 if (!context->IsValid()) {
275 FML_LOG(ERROR) << "Could not create Metal context.";
276 return nullptr;
277 }
278 return context;
279}
280
281ContextMTL::~ContextMTL() {
282 is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
283}
284
285Context::BackendType ContextMTL::GetBackendType() const {
286 return Context::BackendType::kMetal;
287}
288
289// |Context|
290std::string ContextMTL::DescribeGpuModel() const {
291 return std::string([[device_ name] UTF8String]);
292}
293
294// |Context|
295bool ContextMTL::IsValid() const {
296 return is_valid_;
297}
298
299// |Context|
300std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary() const {
301 return shader_library_;
302}
303
304// |Context|
305std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary() const {
306 return pipeline_library_;
307}
308
309// |Context|
310std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary() const {
311 return sampler_library_;
312}
313
314// |Context|
315std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBuffer() const {
316 return CreateCommandBufferInQueue(command_queue_);
317}
318
319// |Context|
320void ContextMTL::Shutdown() {}
321
322#ifdef IMPELLER_DEBUG
323std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer() const {
324 return gpu_tracer_;
325}
326#endif // IMPELLER_DEBUG
327
328std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
329 const {
330 return is_gpu_disabled_sync_switch_;
331}
332
333std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
334 id<MTLCommandQueue> queue) const {
335 if (!IsValid()) {
336 return nullptr;
337 }
338
339 auto buffer = std::shared_ptr<CommandBufferMTL>(
340 new CommandBufferMTL(weak_from_this(), queue));
341 if (!buffer->IsValid()) {
342 return nullptr;
343 }
344 return buffer;
345}
346
347std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator() const {
348 return resource_allocator_;
349}
350
351id<MTLDevice> ContextMTL::GetMTLDevice() const {
352 return device_;
353}
354
355const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities() const {
356 return device_capabilities_;
357}
358
359void ContextMTL::SetCapabilities(
360 const std::shared_ptr<const Capabilities>& capabilities) {
361 device_capabilities_ = capabilities;
362}
363
364// |Context|
365bool ContextMTL::UpdateOffscreenLayerPixelFormat(PixelFormat format) {
366 device_capabilities_ = InferMetalCapabilities(device_, format);
367 return true;
368}
369
370id<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
379void ContextMTL::StoreTaskForGPU(const std::function<void()>& task) {
380 tasks_awaiting_gpu_.emplace_back(task);
381 while (tasks_awaiting_gpu_.size() > kMaxTasksAwaitingGPU) {
382 tasks_awaiting_gpu_.front()();
383 tasks_awaiting_gpu_.pop_front();
384 }
385}
386
387void ContextMTL::FlushTasksAwaitingGPU() {
388 for (const auto& task : tasks_awaiting_gpu_) {
389 task();
390 }
391 tasks_awaiting_gpu_.clear();
392}
393
394ContextMTL::SyncSwitchObserver::SyncSwitchObserver(ContextMTL& parent)
395 : parent_(parent) {}
396
397void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(bool new_is_disabled) {
398 if (!new_is_disabled) {
399 parent_.FlushTasksAwaitingGPU();
400 }
401}
402
403// |Context|
404std::shared_ptr<CommandQueue> ContextMTL::GetCommandQueue() const {
405 return command_queue_ip_;
406}
407
408} // namespace impeller
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 & SetSupportsBufferToTextureBlits(bool 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.
VkDevice device
Definition main.cc:53
static const uint8_t buffer[]
const uint8_t uint32_t uint32_t GError ** error
uint32_t uint32_t * format
#define FML_LOG(severity)
Definition logging.h:82
const char * name
Definition fuchsia.cc:50
bool IsFile(const std::string &path)
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFilePaths(id< MTLDevice > device, const std::vector< std::string > &libraries_paths)
static id< MTLCommandQueue > CreateMetalCommandQueue(id< MTLDevice > device)
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition formats.h:100
static id< MTLDevice > CreateMetalDevice()
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFileData(id< MTLDevice > device, const std::vector< std::shared_ptr< fml::Mapping > > &libraries_data, const std::string &label)
static bool DeviceSupportsComputeSubgroups(id< MTLDevice > device)
static bool DeviceSupportsFramebufferFetch(id< MTLDevice > device)
static std::unique_ptr< Capabilities > InferMetalCapabilities(id< MTLDevice > device, PixelFormat color_format)
Definition ref_ptr.h:256
#define ERROR(message)
#define VALIDATION_LOG
Definition validation.h:73