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
32 return [device supportsFamily:MTLGPUFamilyApple2];
39 return [device supportsFamily:MTLGPUFamilyApple7] ||
40 [device supportsFamily:MTLGPUFamilyMac2];
49 return [device supportsFamily:MTLGPUFamilyApple3];
56 return [device supportsFamily:MTLGPUFamilyMac2] ||
57 [device supportsFamily:MTLGPUFamilyApple7];
63 return [device supportsFamily:MTLGPUFamilyApple2];
68 return [device supportsFamily:MTLGPUFamilyApple6];
106 #if FML_OS_IOS && !TARGET_OS_SIMULATOR
114 ContextMTL::ContextMTL(
116 id<MTLDevice> device,
117 id<MTLCommandQueue> command_queue,
118 NSArray<id<MTLLibrary>>* shader_libraries,
119 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
120 std::optional<PixelFormat> pixel_format_override)
123 command_queue_(command_queue),
124 is_gpu_disabled_sync_switch_(
std::move(is_gpu_disabled_sync_switch)) {
131 sync_switch_observer_.reset(
new SyncSwitchObserver(*
this));
132 is_gpu_disabled_sync_switch_->AddObserver(sync_switch_observer_.get());
136 if (shader_libraries == nil) {
142 auto library = std::shared_ptr<ShaderLibraryMTL>(
143 new ShaderLibraryMTL(shader_libraries));
144 if (!library->IsValid()) {
148 shader_library_ = std::move(library);
154 std::shared_ptr<PipelineLibraryMTL>(
new PipelineLibraryMTL(device_));
160 std::shared_ptr<SamplerLibraryMTL>(
new SamplerLibraryMTL(device_));
165 resource_allocator_ = std::shared_ptr<AllocatorMTL>(
166 new AllocatorMTL(device_,
"Impeller Permanents Allocator"));
167 if (!resource_allocator_) {
173 device_capabilities_ =
175 ? pixel_format_override.value()
177 command_queue_ip_ = std::make_shared<CommandQueue>();
178 #ifdef IMPELLER_DEBUG
179 gpu_tracer_ = std::make_shared<GPUTracerMTL>();
180 capture_manager_ = std::make_shared<ImpellerMetalCaptureManager>(device_);
186 id<MTLDevice> device,
187 const std::vector<std::string>& libraries_paths) {
188 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
189 for (
const auto& library_path : libraries_paths) {
190 if (!fml::IsFile(library_path)) {
192 << library_path <<
"'";
195 NSError* shader_library_error = nil;
196 auto library = [device newLibraryWithFile:@(library_path.c_str())
197 error:&shader_library_error];
199 FML_LOG(ERROR) <<
"Could not create shader library: "
200 << shader_library_error.localizedDescription.UTF8String;
203 [found_libraries addObject:library];
205 return found_libraries;
209 id<MTLDevice> device,
210 const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data,
211 const std::string& label) {
212 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
213 for (
const auto& library_data : libraries_data) {
214 if (library_data ==
nullptr) {
215 FML_LOG(ERROR) <<
"Shader library data was null.";
219 __block
auto data = library_data;
222 ::dispatch_data_create(library_data->GetMapping(),
223 library_data->GetSize(),
224 dispatch_get_main_queue(),
230 if (!dispatch_data) {
231 FML_LOG(ERROR) <<
"Could not wrap shader data in dispatch data.";
235 NSError* shader_library_error = nil;
236 auto library = [device newLibraryWithData:dispatch_data
237 error:&shader_library_error];
239 FML_LOG(ERROR) <<
"Could not create shader library: "
240 << shader_library_error.localizedDescription.UTF8String;
243 if (!label.empty()) {
244 library.label = @(label.c_str());
246 [found_libraries addObject:library];
248 return found_libraries;
252 return ::MTLCreateSystemDefaultDevice();
256 auto command_queue = device.newCommandQueue;
257 if (!command_queue) {
261 command_queue.label =
@"Impeller Command Queue";
262 return command_queue;
267 const std::vector<std::string>& shader_library_paths,
268 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
271 if (!command_queue) {
275 flags, device, command_queue,
277 std::move(is_gpu_disabled_sync_switch)));
279 FML_LOG(ERROR) <<
"Could not create Metal context.";
287 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
288 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
289 const std::string& library_label,
290 std::optional<PixelFormat> pixel_format_override) {
293 if (!command_queue) {
297 flags, device, command_queue,
300 std::move(is_gpu_disabled_sync_switch), pixel_format_override));
302 FML_LOG(ERROR) <<
"Could not create Metal context.";
310 id<MTLDevice> device,
311 id<MTLCommandQueue> command_queue,
312 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
313 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
314 const std::string& library_label) {
315 auto context = std::shared_ptr<ContextMTL>(
319 std::move(is_gpu_disabled_sync_switch)));
321 FML_LOG(ERROR) <<
"Could not create Metal context.";
327 ContextMTL::~ContextMTL() {
328 is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
332 return Context::BackendType::kMetal;
336 std::string ContextMTL::DescribeGpuModel()
const {
337 return std::string([[device_ name] UTF8String]);
341 bool ContextMTL::IsValid()
const {
346 std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary()
const {
347 return shader_library_;
351 std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary()
const {
352 return pipeline_library_;
356 std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary()
const {
357 return sampler_library_;
362 return CreateCommandBufferInQueue(command_queue_);
366 void ContextMTL::Shutdown() {}
368 #ifdef IMPELLER_DEBUG
369 std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer()
const {
374 std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
376 return is_gpu_disabled_sync_switch_;
379 std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
380 id<MTLCommandQueue> queue)
const {
385 auto buffer = std::shared_ptr<CommandBufferMTL>(
386 new CommandBufferMTL(weak_from_this(), device_, queue));
387 if (!buffer->IsValid()) {
393 std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator()
const {
394 return resource_allocator_;
397 id<MTLDevice> ContextMTL::GetMTLDevice()
const {
401 const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities()
const {
402 return device_capabilities_;
405 void ContextMTL::SetCapabilities(
406 const std::shared_ptr<const Capabilities>& capabilities) {
407 device_capabilities_ = capabilities;
411 bool ContextMTL::UpdateOffscreenLayerPixelFormat(
PixelFormat format) {
416 id<MTLCommandBuffer> ContextMTL::CreateMTLCommandBuffer(
417 const std::string& label)
const {
418 auto buffer = [command_queue_ commandBuffer];
419 if (!label.empty()) {
420 [buffer setLabel:@(label.data())];
425 void ContextMTL::StoreTaskForGPU(
const fml::closure& task,
426 const fml::closure& failure) {
427 std::vector<PendingTasks> failed_tasks;
429 Lock lock(tasks_awaiting_gpu_mutex_);
430 tasks_awaiting_gpu_.push_back(PendingTasks{task, failure});
431 int32_t failed_task_count =
432 tasks_awaiting_gpu_.size() - kMaxTasksAwaitingGPU;
433 if (failed_task_count > 0) {
434 failed_tasks.reserve(failed_task_count);
435 failed_tasks.insert(failed_tasks.end(),
436 std::make_move_iterator(tasks_awaiting_gpu_.begin()),
437 std::make_move_iterator(tasks_awaiting_gpu_.begin() +
439 tasks_awaiting_gpu_.erase(
440 tasks_awaiting_gpu_.begin(),
441 tasks_awaiting_gpu_.begin() + failed_task_count);
444 for (
const PendingTasks& task : failed_tasks) {
451 void ContextMTL::FlushTasksAwaitingGPU() {
452 std::deque<PendingTasks> tasks_awaiting_gpu;
454 Lock lock(tasks_awaiting_gpu_mutex_);
455 std::swap(tasks_awaiting_gpu, tasks_awaiting_gpu_);
457 std::vector<PendingTasks> tasks_to_queue;
458 for (
const auto& task : tasks_awaiting_gpu) {
459 is_gpu_disabled_sync_switch_->Execute(fml::SyncSwitch::Handlers()
460 .SetIfFalse([&] { task.task(); })
470 tasks_to_queue.push_back(task);
473 if (!tasks_to_queue.empty()) {
474 Lock lock(tasks_awaiting_gpu_mutex_);
475 tasks_awaiting_gpu_.insert(tasks_awaiting_gpu_.end(),
476 tasks_to_queue.begin(), tasks_to_queue.end());
480 bool ContextMTL::FinishQueue() {
482 ContextMTL::Cast(
this)->CreateMTLCommandBuffer(
"Finish Queue Waiter");
494 ContextMTL::SyncSwitchObserver::SyncSwitchObserver(
ContextMTL& parent)
497 void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(
bool new_is_disabled) {
498 if (!new_is_disabled) {
499 parent_.FlushTasksAwaitingGPU();
505 return command_queue_ip_;
513 #ifdef IMPELLER_DEBUG
514 const std::shared_ptr<ImpellerMetalCaptureManager>
515 ContextMTL::GetCaptureManager()
const {
516 return capture_manager_;
521 current_capture_scope_ = [[MTLCaptureManager sharedCaptureManager]
522 newCaptureScopeWithDevice:device];
523 [current_capture_scope_ setLabel:
@"Impeller Frame"];
524 [[MTLCaptureManager sharedCaptureManager]
525 setDefaultCaptureScope:current_capture_scope_];
529 return scope_active_;
536 scope_active_ =
true;
537 [current_capture_scope_ beginScope];
541 FML_DCHECK(scope_active_);
542 [current_capture_scope_ endScope];
543 scope_active_ =
false;
CapabilitiesBuilder & SetDefaultColorFormat(PixelFormat value)
CapabilitiesBuilder & SetSupportsComputeSubgroups(bool value)
CapabilitiesBuilder & SetMinimumUniformAlignment(size_t value)
CapabilitiesBuilder & SetSupportsTextureToTextureBlits(bool value)
CapabilitiesBuilder & SetDefaultStencilFormat(PixelFormat value)
CapabilitiesBuilder & SetSupportsDeviceTransientTextures(bool value)
CapabilitiesBuilder & SetSupportsTriangleFan(bool value)
CapabilitiesBuilder & SetMaxSamplerAnisotropy(uint32_t value)
CapabilitiesBuilder & SetSupportsFramebufferFetch(bool value)
CapabilitiesBuilder & SetSupportsDecalSamplerAddressMode(bool value)
CapabilitiesBuilder & SetSupportsTextureCompression(CompressedTextureFamily family, 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 bool DeviceSupportsTextureCompressionMobile(id< MTLDevice > device)
static id< MTLDevice > CreateMetalDevice()
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
@ kASTCHDR
ASTC HDR. A separate device feature from ASTC LDR.
@ kBC
S3TC, RGTC, and BPTC (BC1 through BC7). Desktop GPUs.
@ kETC2
ETC2 and EAC. Mobile, OpenGL ES 3.0, and WebGL2.
@ kASTC
ASTC LDR. Modern mobile and some desktop.
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 DeviceSupportsTextureCompressionAstcHdr(id< MTLDevice > device)
static bool DeviceSupportsComputeSubgroups(id< MTLDevice > device)
static bool DeviceSupportsFramebufferFetch(id< MTLDevice > device)
static bool DeviceSupportsTextureCompressionBC(id< MTLDevice > device)
std::shared_ptr< ContextGLES > context
std::shared_ptr< CommandBuffer > command_buffer