6 #include <Metal/Metal.h>
10 #include "flutter/fml/concurrent_message_loop.h"
11 #include "flutter/fml/file.h"
12 #include "flutter/fml/logging.h"
13 #include "flutter/fml/paths.h"
14 #include "flutter/fml/synchronization/sync_switch.h"
25 #if FML_OS_IOS_SIMULATOR
27 #else // FML_OS_IOS_SIMULATOR
29 if (@available(macOS 10.15, iOS 13, tvOS 13, *)) {
30 return [device supportsFamily:MTLGPUFamilyApple2];
36 return [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1];
40 #endif // FML_OS_IOS_SIMULATOR
44 bool supports_subgroups =
false;
47 if (@available(ios 13.0, tvos 13.0, macos 10.15, *)) {
48 supports_subgroups = [device supportsFamily:MTLGPUFamilyApple7] ||
49 [device supportsFamily:MTLGPUFamilyMac2];
51 return supports_subgroups;
76 ContextMTL::ContextMTL(
78 id<MTLCommandQueue> command_queue,
79 NSArray<id<MTLLibrary>>* shader_libraries,
80 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
81 std::optional<PixelFormat> pixel_format_override)
83 command_queue_(command_queue),
84 is_gpu_disabled_sync_switch_(
std::move(is_gpu_disabled_sync_switch)) {
91 sync_switch_observer_.reset(
new SyncSwitchObserver(*
this));
92 is_gpu_disabled_sync_switch_->AddObserver(sync_switch_observer_.get());
96 if (shader_libraries == nil) {
102 auto library = std::shared_ptr<ShaderLibraryMTL>(
103 new ShaderLibraryMTL(shader_libraries));
104 if (!library->IsValid()) {
108 shader_library_ = std::move(library);
114 std::shared_ptr<PipelineLibraryMTL>(
new PipelineLibraryMTL(device_));
120 std::shared_ptr<SamplerLibraryMTL>(
new SamplerLibraryMTL(device_));
125 resource_allocator_ = std::shared_ptr<AllocatorMTL>(
126 new AllocatorMTL(device_,
"Impeller Permanents Allocator"));
127 if (!resource_allocator_) {
133 device_capabilities_ =
135 ? pixel_format_override.value()
137 command_queue_ip_ = std::make_shared<CommandQueue>();
138 #ifdef IMPELLER_DEBUG
139 gpu_tracer_ = std::make_shared<GPUTracerMTL>();
140 capture_manager_ = std::make_shared<ImpellerMetalCaptureManager>(device_);
141 #endif // IMPELLER_DEBUG
146 id<MTLDevice> device,
147 const std::vector<std::string>& libraries_paths) {
148 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
149 for (
const auto& library_path : libraries_paths) {
150 if (!fml::IsFile(library_path)) {
152 << library_path <<
"'";
155 NSError* shader_library_error = nil;
156 auto library = [device newLibraryWithFile:@(library_path.c_str())
157 error:&shader_library_error];
159 FML_LOG(ERROR) <<
"Could not create shader library: "
160 << shader_library_error.localizedDescription.UTF8String;
163 [found_libraries addObject:library];
165 return found_libraries;
169 id<MTLDevice> device,
170 const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data,
171 const std::string& label) {
172 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
173 for (
const auto& library_data : libraries_data) {
174 if (library_data ==
nullptr) {
175 FML_LOG(ERROR) <<
"Shader library data was null.";
179 __block
auto data = library_data;
182 ::dispatch_data_create(library_data->GetMapping(),
183 library_data->GetSize(),
184 dispatch_get_main_queue(),
190 if (!dispatch_data) {
191 FML_LOG(ERROR) <<
"Could not wrap shader data in dispatch data.";
195 NSError* shader_library_error = nil;
196 auto library = [device newLibraryWithData:dispatch_data
197 error:&shader_library_error];
199 FML_LOG(ERROR) <<
"Could not create shader library: "
200 << shader_library_error.localizedDescription.UTF8String;
203 if (!label.empty()) {
204 library.label = @(label.c_str());
206 [found_libraries addObject:library];
208 return found_libraries;
212 return ::MTLCreateSystemDefaultDevice();
216 auto command_queue = device.newCommandQueue;
217 if (!command_queue) {
221 command_queue.label =
@"Impeller Command Queue";
222 return command_queue;
226 const std::vector<std::string>& shader_library_paths,
227 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
230 if (!command_queue) {
233 auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(
234 device, command_queue,
236 std::move(is_gpu_disabled_sync_switch)));
237 if (!context->IsValid()) {
238 FML_LOG(ERROR) <<
"Could not create Metal context.";
245 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
246 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
247 const std::string& library_label,
248 std::optional<PixelFormat> pixel_format_override) {
251 if (!command_queue) {
254 auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(
255 device, command_queue,
258 std::move(is_gpu_disabled_sync_switch), pixel_format_override));
259 if (!context->IsValid()) {
260 FML_LOG(ERROR) <<
"Could not create Metal context.";
267 id<MTLDevice> device,
268 id<MTLCommandQueue> command_queue,
269 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
270 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
271 const std::string& library_label) {
272 auto context = std::shared_ptr<ContextMTL>(
276 std::move(is_gpu_disabled_sync_switch)));
277 if (!context->IsValid()) {
278 FML_LOG(ERROR) <<
"Could not create Metal context.";
284 ContextMTL::~ContextMTL() {
285 is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
289 return Context::BackendType::kMetal;
293 std::string ContextMTL::DescribeGpuModel()
const {
294 return std::string([[device_ name] UTF8String]);
298 bool ContextMTL::IsValid()
const {
303 std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary()
const {
304 return shader_library_;
308 std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary()
const {
309 return pipeline_library_;
313 std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary()
const {
314 return sampler_library_;
319 return CreateCommandBufferInQueue(command_queue_);
323 void ContextMTL::Shutdown() {}
325 #ifdef IMPELLER_DEBUG
326 std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer()
const {
329 #endif // IMPELLER_DEBUG
331 std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
333 return is_gpu_disabled_sync_switch_;
336 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
337 id<MTLCommandQueue> queue)
const {
342 auto buffer = std::shared_ptr<CommandBufferMTL>(
343 new CommandBufferMTL(weak_from_this(), device_, queue));
344 if (!buffer->IsValid()) {
350 std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator()
const {
351 return resource_allocator_;
354 id<MTLDevice> ContextMTL::GetMTLDevice()
const {
358 const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities()
const {
359 return device_capabilities_;
362 void ContextMTL::SetCapabilities(
363 const std::shared_ptr<const Capabilities>& capabilities) {
364 device_capabilities_ = capabilities;
368 bool ContextMTL::UpdateOffscreenLayerPixelFormat(
PixelFormat format) {
373 id<MTLCommandBuffer> ContextMTL::CreateMTLCommandBuffer(
374 const std::string& label)
const {
375 auto buffer = [command_queue_ commandBuffer];
376 if (!label.empty()) {
377 [buffer setLabel:@(label.data())];
382 void ContextMTL::StoreTaskForGPU(
const fml::closure& task,
383 const fml::closure& failure) {
384 std::vector<PendingTasks> failed_tasks;
386 Lock lock(tasks_awaiting_gpu_mutex_);
387 tasks_awaiting_gpu_.push_back(PendingTasks{task, failure});
388 int32_t failed_task_count =
389 tasks_awaiting_gpu_.size() - kMaxTasksAwaitingGPU;
390 if (failed_task_count > 0) {
391 failed_tasks.reserve(failed_task_count);
392 failed_tasks.insert(failed_tasks.end(),
393 std::make_move_iterator(tasks_awaiting_gpu_.begin()),
394 std::make_move_iterator(tasks_awaiting_gpu_.begin() +
396 tasks_awaiting_gpu_.erase(
397 tasks_awaiting_gpu_.begin(),
398 tasks_awaiting_gpu_.begin() + failed_task_count);
401 for (
const PendingTasks& task : failed_tasks) {
408 void ContextMTL::FlushTasksAwaitingGPU() {
409 std::deque<PendingTasks> tasks_awaiting_gpu;
411 Lock lock(tasks_awaiting_gpu_mutex_);
412 std::swap(tasks_awaiting_gpu, tasks_awaiting_gpu_);
414 for (
const auto& task : tasks_awaiting_gpu) {
419 ContextMTL::SyncSwitchObserver::SyncSwitchObserver(ContextMTL& parent)
422 void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(
bool new_is_disabled) {
423 if (!new_is_disabled) {
424 parent_.FlushTasksAwaitingGPU();
430 return command_queue_ip_;
433 #ifdef IMPELLER_DEBUG
434 const std::shared_ptr<ImpellerMetalCaptureManager>
435 ContextMTL::GetCaptureManager()
const {
436 return capture_manager_;
438 #endif // IMPELLER_DEBUG
441 current_capture_scope_ = [[MTLCaptureManager sharedCaptureManager]
442 newCaptureScopeWithDevice:device];
443 [current_capture_scope_ setLabel:
@"Impeller Frame"];
447 return scope_active_;
454 scope_active_ =
true;
455 [current_capture_scope_ beginScope];
459 FML_DCHECK(scope_active_);
460 [current_capture_scope_ endScope];
461 scope_active_ =
false;