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"
24 #if FML_OS_IOS_SIMULATOR
26 #else // FML_OS_IOS_SIMULATOR
28 if (@available(macOS 10.15, iOS 13, tvOS 13, *)) {
29 return [device supportsFamily:MTLGPUFamilyApple2];
35 return [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1];
39 #endif // FML_OS_IOS_SIMULATOR
43 bool supports_subgroups =
false;
46 if (@available(ios 13.0, tvos 13.0, macos 10.15, *)) {
47 supports_subgroups = [device supportsFamily:MTLGPUFamilyApple7] ||
48 [device supportsFamily:MTLGPUFamilyMac2];
50 return supports_subgroups;
73 ContextMTL::ContextMTL(
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)
80 command_queue_(command_queue),
81 is_gpu_disabled_sync_switch_(
std::move(is_gpu_disabled_sync_switch)) {
88 sync_switch_observer_.reset(
new SyncSwitchObserver(*
this));
89 is_gpu_disabled_sync_switch_->AddObserver(sync_switch_observer_.get());
93 if (shader_libraries == nil) {
99 auto library = std::shared_ptr<ShaderLibraryMTL>(
100 new ShaderLibraryMTL(shader_libraries));
101 if (!library->IsValid()) {
105 shader_library_ = std::move(library);
111 std::shared_ptr<PipelineLibraryMTL>(
new PipelineLibraryMTL(device_));
117 std::shared_ptr<SamplerLibraryMTL>(
new SamplerLibraryMTL(device_));
122 resource_allocator_ = std::shared_ptr<AllocatorMTL>(
123 new AllocatorMTL(device_,
"Impeller Permanents Allocator"));
124 if (!resource_allocator_) {
130 device_capabilities_ =
132 ? pixel_format_override.value()
134 command_queue_ip_ = std::make_shared<CommandQueue>();
135 #ifdef IMPELLER_DEBUG
136 gpu_tracer_ = std::make_shared<GPUTracerMTL>();
137 #endif // IMPELLER_DEBUG
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)) {
148 << library_path <<
"'";
151 NSError* shader_library_error = nil;
152 auto library = [device newLibraryWithFile:@(library_path.c_str())
153 error:&shader_library_error];
155 FML_LOG(ERROR) <<
"Could not create shader library: "
156 << shader_library_error.localizedDescription.UTF8String;
159 [found_libraries addObject:library];
161 return found_libraries;
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.";
175 __block
auto data = library_data;
178 ::dispatch_data_create(library_data->GetMapping(),
179 library_data->GetSize(),
180 dispatch_get_main_queue(),
186 if (!dispatch_data) {
187 FML_LOG(ERROR) <<
"Could not wrap shader data in dispatch data.";
191 NSError* shader_library_error = nil;
192 auto library = [device newLibraryWithData:dispatch_data
193 error:&shader_library_error];
195 FML_LOG(ERROR) <<
"Could not create shader library: "
196 << shader_library_error.localizedDescription.UTF8String;
199 if (!label.empty()) {
200 library.label = @(label.c_str());
202 [found_libraries addObject:library];
204 return found_libraries;
208 return ::MTLCreateSystemDefaultDevice();
212 auto command_queue = device.newCommandQueue;
213 if (!command_queue) {
217 command_queue.label =
@"Impeller Command Queue";
218 return command_queue;
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) {
226 if (!command_queue) {
229 auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(
230 device, command_queue,
232 std::move(is_gpu_disabled_sync_switch)));
233 if (!context->IsValid()) {
234 FML_LOG(ERROR) <<
"Could not create Metal context.";
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) {
247 if (!command_queue) {
250 auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(
251 device, command_queue,
254 std::move(is_gpu_disabled_sync_switch), pixel_format_override));
255 if (!context->IsValid()) {
256 FML_LOG(ERROR) <<
"Could not create Metal context.";
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>(
272 std::move(is_gpu_disabled_sync_switch)));
273 if (!context->IsValid()) {
274 FML_LOG(ERROR) <<
"Could not create Metal context.";
280 ContextMTL::~ContextMTL() {
281 is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
285 return Context::BackendType::kMetal;
289 std::string ContextMTL::DescribeGpuModel()
const {
290 return std::string([[device_ name] UTF8String]);
294 bool ContextMTL::IsValid()
const {
299 std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary()
const {
300 return shader_library_;
304 std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary()
const {
305 return pipeline_library_;
309 std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary()
const {
310 return sampler_library_;
315 return CreateCommandBufferInQueue(command_queue_);
319 void ContextMTL::Shutdown() {}
321 #ifdef IMPELLER_DEBUG
322 std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer()
const {
325 #endif // IMPELLER_DEBUG
327 std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
329 return is_gpu_disabled_sync_switch_;
332 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
333 id<MTLCommandQueue> queue)
const {
338 auto buffer = std::shared_ptr<CommandBufferMTL>(
339 new CommandBufferMTL(weak_from_this(), queue));
340 if (!buffer->IsValid()) {
346 std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator()
const {
347 return resource_allocator_;
350 id<MTLDevice> ContextMTL::GetMTLDevice()
const {
354 const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities()
const {
355 return device_capabilities_;
358 void ContextMTL::SetCapabilities(
359 const std::shared_ptr<const Capabilities>& capabilities) {
360 device_capabilities_ = capabilities;
364 bool ContextMTL::UpdateOffscreenLayerPixelFormat(
PixelFormat format) {
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())];
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();
386 void ContextMTL::FlushTasksAwaitingGPU() {
387 for (
const auto& task : tasks_awaiting_gpu_) {
390 tasks_awaiting_gpu_.clear();
393 ContextMTL::SyncSwitchObserver::SyncSwitchObserver(ContextMTL& parent)
396 void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(
bool new_is_disabled) {
397 if (!new_is_disabled) {
398 parent_.FlushTasksAwaitingGPU();
404 return command_queue_ip_;