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"
26 #if FML_OS_IOS_SIMULATOR
30 if (@available(macOS 10.15, iOS 13, tvOS 13, *)) {
31 return [device supportsFamily:MTLGPUFamilyApple2];
37 return [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1];
45 bool supports_subgroups =
false;
48 if (@available(ios 13.0, tvos 13.0, macos 10.15, *)) {
49 supports_subgroups = [device supportsFamily:MTLGPUFamilyApple7] ||
50 [device supportsFamily:MTLGPUFamilyMac2];
52 return supports_subgroups;
58 if (@available(macOS 10.15, iOS 13, tvOS 13, *)) {
59 return [device supportsFamily:MTLGPUFamilyApple3];
88 ContextMTL::ContextMTL(
91 id<MTLCommandQueue> command_queue,
92 NSArray<id<MTLLibrary>>* shader_libraries,
93 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
94 std::optional<PixelFormat> pixel_format_override)
97 command_queue_(command_queue),
98 is_gpu_disabled_sync_switch_(
std::move(is_gpu_disabled_sync_switch)) {
105 sync_switch_observer_.reset(
new SyncSwitchObserver(*
this));
106 is_gpu_disabled_sync_switch_->AddObserver(sync_switch_observer_.get());
110 if (shader_libraries == nil) {
116 auto library = std::shared_ptr<ShaderLibraryMTL>(
117 new ShaderLibraryMTL(shader_libraries));
118 if (!library->IsValid()) {
122 shader_library_ = std::move(library);
128 std::shared_ptr<PipelineLibraryMTL>(
new PipelineLibraryMTL(device_));
134 std::shared_ptr<SamplerLibraryMTL>(
new SamplerLibraryMTL(device_));
139 resource_allocator_ = std::shared_ptr<AllocatorMTL>(
140 new AllocatorMTL(device_,
"Impeller Permanents Allocator"));
141 if (!resource_allocator_) {
147 device_capabilities_ =
149 ? pixel_format_override.value()
151 command_queue_ip_ = std::make_shared<CommandQueue>();
152 #ifdef IMPELLER_DEBUG
153 gpu_tracer_ = std::make_shared<GPUTracerMTL>();
154 capture_manager_ = std::make_shared<ImpellerMetalCaptureManager>(device_);
160 id<MTLDevice> device,
161 const std::vector<std::string>& libraries_paths) {
162 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
163 for (
const auto& library_path : libraries_paths) {
164 if (!fml::IsFile(library_path)) {
166 << library_path <<
"'";
169 NSError* shader_library_error = nil;
170 auto library = [device newLibraryWithFile:@(library_path.c_str())
171 error:&shader_library_error];
173 FML_LOG(ERROR) <<
"Could not create shader library: "
174 << shader_library_error.localizedDescription.UTF8String;
177 [found_libraries addObject:library];
179 return found_libraries;
183 id<MTLDevice> device,
184 const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data,
185 const std::string& label) {
186 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
187 for (
const auto& library_data : libraries_data) {
188 if (library_data ==
nullptr) {
189 FML_LOG(ERROR) <<
"Shader library data was null.";
193 __block
auto data = library_data;
196 ::dispatch_data_create(library_data->GetMapping(),
197 library_data->GetSize(),
198 dispatch_get_main_queue(),
204 if (!dispatch_data) {
205 FML_LOG(ERROR) <<
"Could not wrap shader data in dispatch data.";
209 NSError* shader_library_error = nil;
210 auto library = [device newLibraryWithData:dispatch_data
211 error:&shader_library_error];
213 FML_LOG(ERROR) <<
"Could not create shader library: "
214 << shader_library_error.localizedDescription.UTF8String;
217 if (!label.empty()) {
218 library.label = @(label.c_str());
220 [found_libraries addObject:library];
222 return found_libraries;
226 return ::MTLCreateSystemDefaultDevice();
230 auto command_queue = device.newCommandQueue;
231 if (!command_queue) {
235 command_queue.label =
@"Impeller Command Queue";
236 return command_queue;
241 const std::vector<std::string>& shader_library_paths,
242 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
245 if (!command_queue) {
248 auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(
249 flags, device, command_queue,
251 std::move(is_gpu_disabled_sync_switch)));
252 if (!context->IsValid()) {
253 FML_LOG(ERROR) <<
"Could not create Metal context.";
261 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
262 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
263 const std::string& library_label,
264 std::optional<PixelFormat> pixel_format_override) {
267 if (!command_queue) {
270 auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(
271 flags, device, command_queue,
274 std::move(is_gpu_disabled_sync_switch), pixel_format_override));
275 if (!context->IsValid()) {
276 FML_LOG(ERROR) <<
"Could not create Metal context.";
284 id<MTLDevice> device,
285 id<MTLCommandQueue> command_queue,
286 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
287 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
288 const std::string& library_label) {
289 auto context = std::shared_ptr<ContextMTL>(
293 std::move(is_gpu_disabled_sync_switch)));
294 if (!context->IsValid()) {
295 FML_LOG(ERROR) <<
"Could not create Metal context.";
301 ContextMTL::~ContextMTL() {
302 is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
306 return Context::BackendType::kMetal;
310 std::string ContextMTL::DescribeGpuModel()
const {
311 return std::string([[device_ name] UTF8String]);
315 bool ContextMTL::IsValid()
const {
320 std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary()
const {
321 return shader_library_;
325 std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary()
const {
326 return pipeline_library_;
330 std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary()
const {
331 return sampler_library_;
336 return CreateCommandBufferInQueue(command_queue_);
340 void ContextMTL::Shutdown() {}
342 #ifdef IMPELLER_DEBUG
343 std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer()
const {
348 std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
350 return is_gpu_disabled_sync_switch_;
353 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
354 id<MTLCommandQueue> queue)
const {
359 auto buffer = std::shared_ptr<CommandBufferMTL>(
360 new CommandBufferMTL(weak_from_this(), device_, queue));
361 if (!buffer->IsValid()) {
367 std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator()
const {
368 return resource_allocator_;
371 id<MTLDevice> ContextMTL::GetMTLDevice()
const {
375 const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities()
const {
376 return device_capabilities_;
379 void ContextMTL::SetCapabilities(
380 const std::shared_ptr<const Capabilities>& capabilities) {
381 device_capabilities_ = capabilities;
385 bool ContextMTL::UpdateOffscreenLayerPixelFormat(
PixelFormat format) {
390 id<MTLCommandBuffer> ContextMTL::CreateMTLCommandBuffer(
391 const std::string& label)
const {
392 auto buffer = [command_queue_ commandBuffer];
393 if (!label.empty()) {
394 [buffer setLabel:@(label.data())];
399 void ContextMTL::StoreTaskForGPU(
const fml::closure& task,
400 const fml::closure& failure) {
401 std::vector<PendingTasks> failed_tasks;
403 Lock lock(tasks_awaiting_gpu_mutex_);
404 tasks_awaiting_gpu_.push_back(PendingTasks{task, failure});
405 int32_t failed_task_count =
406 tasks_awaiting_gpu_.size() - kMaxTasksAwaitingGPU;
407 if (failed_task_count > 0) {
408 failed_tasks.reserve(failed_task_count);
409 failed_tasks.insert(failed_tasks.end(),
410 std::make_move_iterator(tasks_awaiting_gpu_.begin()),
411 std::make_move_iterator(tasks_awaiting_gpu_.begin() +
413 tasks_awaiting_gpu_.erase(
414 tasks_awaiting_gpu_.begin(),
415 tasks_awaiting_gpu_.begin() + failed_task_count);
418 for (
const PendingTasks& task : failed_tasks) {
425 void ContextMTL::FlushTasksAwaitingGPU() {
426 std::deque<PendingTasks> tasks_awaiting_gpu;
428 Lock lock(tasks_awaiting_gpu_mutex_);
429 std::swap(tasks_awaiting_gpu, tasks_awaiting_gpu_);
431 for (
const auto& task : tasks_awaiting_gpu) {
436 ContextMTL::SyncSwitchObserver::SyncSwitchObserver(ContextMTL& parent)
439 void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(
bool new_is_disabled) {
440 if (!new_is_disabled) {
441 parent_.FlushTasksAwaitingGPU();
447 return command_queue_ip_;
455 #ifdef IMPELLER_DEBUG
456 const std::shared_ptr<ImpellerMetalCaptureManager>
457 ContextMTL::GetCaptureManager()
const {
458 return capture_manager_;
463 current_capture_scope_ = [[MTLCaptureManager sharedCaptureManager]
464 newCaptureScopeWithDevice:device];
465 [current_capture_scope_ setLabel:
@"Impeller Frame"];
469 return scope_active_;
476 scope_active_ =
true;
477 [current_capture_scope_ beginScope];
481 FML_DCHECK(scope_active_);
482 [current_capture_scope_ endScope];
483 scope_active_ =
false;
CapabilitiesBuilder & SetDefaultColorFormat(PixelFormat value)
CapabilitiesBuilder & SetSupportsComputeSubgroups(bool value)
CapabilitiesBuilder & SetSupportsTextureToTextureBlits(bool value)
CapabilitiesBuilder & SetDefaultStencilFormat(PixelFormat value)
CapabilitiesBuilder & SetSupportsDeviceTransientTextures(bool value)
CapabilitiesBuilder & SetSupportsTriangleFan(bool value)
CapabilitiesBuilder & SetSupportsFramebufferFetch(bool value)
CapabilitiesBuilder & SetSupportsDecalSamplerAddressMode(bool value)
CapabilitiesBuilder & SetSupportsOffscreenMSAA(bool value)
CapabilitiesBuilder & SetSupportsSSBO(bool value)
CapabilitiesBuilder & SetMaximumRenderPassAttachmentSize(ISize size)
CapabilitiesBuilder & SetSupportsExtendedRangeFormats(bool value)
CapabilitiesBuilder & SetDefaultGlyphAtlasFormat(PixelFormat 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.
RuntimeStageBackend GetRuntimeStageBackend() const override
Retrieve the runtime stage for this context type.
ScopedObject< Object > Create(CtorArgs &&... args)
static bool DeviceSupportsExtendedRangeFormats(id< MTLDevice > device)
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFileData(id< MTLDevice > device, const std::vector< std::shared_ptr< fml::Mapping >> &libraries_data, const std::string &label)
static id< MTLDevice > CreateMetalDevice()
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
ISize DeviceMaxTextureSizeSupported(id< MTLDevice > device)
static id< MTLCommandBuffer > CreateCommandBuffer(id< MTLCommandQueue > queue)
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFilePaths(id< MTLDevice > device, const std::vector< std::string > &libraries_paths)
static id< MTLCommandQueue > CreateMetalCommandQueue(id< MTLDevice > device)
static std::unique_ptr< Capabilities > InferMetalCapabilities(id< MTLDevice > device, PixelFormat color_format)
static bool DeviceSupportsComputeSubgroups(id< MTLDevice > device)
static bool DeviceSupportsFramebufferFetch(id< MTLDevice > device)
std::shared_ptr< const fml::Mapping > data