7 #include <Foundation/Foundation.h>
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"
22 #if FML_OS_IOS_SIMULATOR
24 #else // FML_OS_IOS_SIMULATOR
26 if (@available(macOS 10.15, iOS 13, tvOS 13, *)) {
27 return [device supportsFamily:MTLGPUFamilyApple2];
33 return [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1];
37 #endif // FML_OS_IOS_SIMULATOR
41 bool supports_subgroups =
false;
44 if (@available(ios 13.0, tvos 13.0, macos 10.15, *)) {
45 supports_subgroups = [device supportsFamily:MTLGPUFamilyApple7] ||
46 [device supportsFamily:MTLGPUFamilyMac2];
48 return supports_subgroups;
72 ContextMTL::ContextMTL(
74 id<MTLCommandQueue> command_queue,
75 NSArray<id<MTLLibrary>>* shader_libraries,
76 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch)
78 command_queue_(command_queue),
79 is_gpu_disabled_sync_switch_(
std::move(is_gpu_disabled_sync_switch)) {
88 raster_message_loop_ = fml::ConcurrentMessageLoop::Create(
89 std::min(4u, std::thread::hardware_concurrency()));
90 raster_message_loop_->PostTaskToAllWorkers([]() {
93 [[NSThread currentThread] setThreadPriority:1.0];
96 pthread_t thread = pthread_self();
97 if (!pthread_getschedparam(thread, &policy, ¶m)) {
98 param.sched_priority = 50;
99 pthread_setschedparam(thread, policy, ¶m);
106 if (shader_libraries == nil) {
112 auto library = std::shared_ptr<ShaderLibraryMTL>(
113 new ShaderLibraryMTL(shader_libraries));
114 if (!library->IsValid()) {
118 shader_library_ = std::move(library);
124 std::shared_ptr<PipelineLibraryMTL>(
new PipelineLibraryMTL(device_));
130 std::shared_ptr<SamplerLibraryMTL>(
new SamplerLibraryMTL(device_));
135 resource_allocator_ = std::shared_ptr<AllocatorMTL>(
136 new AllocatorMTL(device_,
"Impeller Permanents Allocator"));
137 if (!resource_allocator_) {
143 device_capabilities_ =
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)) {
156 << library_path <<
"'";
159 NSError* shader_library_error = nil;
160 auto library = [device newLibraryWithFile:@(library_path.c_str())
161 error:&shader_library_error];
163 FML_LOG(ERROR) <<
"Could not create shader library: "
164 << shader_library_error.localizedDescription.UTF8String;
167 [found_libraries addObject:library];
169 return found_libraries;
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.";
183 __block
auto data = library_data;
186 ::dispatch_data_create(library_data->GetMapping(),
187 library_data->GetSize(),
188 dispatch_get_main_queue(),
194 if (!dispatch_data) {
195 FML_LOG(ERROR) <<
"Could not wrap shader data in dispatch data.";
199 NSError* shader_library_error = nil;
200 auto library = [device newLibraryWithData:dispatch_data
201 error:&shader_library_error];
203 FML_LOG(ERROR) <<
"Could not create shader library: "
204 << shader_library_error.localizedDescription.UTF8String;
207 if (!label.empty()) {
208 library.label = @(label.c_str());
210 [found_libraries addObject:library];
212 return found_libraries;
216 return ::MTLCreateSystemDefaultDevice();
220 auto command_queue = device.newCommandQueue;
221 if (!command_queue) {
225 command_queue.label =
@"Impeller Command Queue";
226 return command_queue;
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) {
234 if (!command_queue) {
237 auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(
238 device, command_queue,
240 std::move(is_gpu_disabled_sync_switch)));
241 if (!context->IsValid()) {
242 FML_LOG(ERROR) <<
"Could not create Metal context.";
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) {
254 if (!command_queue) {
257 auto context = std::shared_ptr<ContextMTL>(
261 std::move(is_gpu_disabled_sync_switch)));
262 if (!context->IsValid()) {
263 FML_LOG(ERROR) <<
"Could not create Metal context.";
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>(
279 std::move(is_gpu_disabled_sync_switch)));
280 if (!context->IsValid()) {
281 FML_LOG(ERROR) <<
"Could not create Metal context.";
287 ContextMTL::~ContextMTL() =
default;
290 return Context::BackendType::kMetal;
294 std::string ContextMTL::DescribeGpuModel()
const {
295 return std::string([[device_ name] UTF8String]);
299 bool ContextMTL::IsValid()
const {
304 std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary()
const {
305 return shader_library_;
309 std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary()
const {
310 return pipeline_library_;
314 std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary()
const {
315 return sampler_library_;
320 return CreateCommandBufferInQueue(command_queue_);
324 void ContextMTL::Shutdown() {
325 raster_message_loop_.reset();
328 const std::shared_ptr<fml::ConcurrentTaskRunner>
329 ContextMTL::GetWorkerTaskRunner()
const {
330 return raster_message_loop_->GetTaskRunner();
333 std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
335 return is_gpu_disabled_sync_switch_;
338 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
339 id<MTLCommandQueue> queue)
const {
344 auto buffer = std::shared_ptr<CommandBufferMTL>(
345 new CommandBufferMTL(weak_from_this(), queue));
346 if (!buffer->IsValid()) {
352 std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator()
const {
353 return resource_allocator_;
356 id<MTLDevice> ContextMTL::GetMTLDevice()
const {
360 const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities()
const {
361 return device_capabilities_;
365 bool ContextMTL::UpdateOffscreenLayerPixelFormat(
PixelFormat format) {
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())];