6#include <Metal/Metal.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];
73#if FML_OS_IOS && !TARGET_OS_SIMULATOR
81ContextMTL::ContextMTL(
84 id<MTLCommandQueue> command_queue,
85 NSArray<id<MTLLibrary>>* shader_libraries,
86 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
87 std::optional<PixelFormat> pixel_format_override)
90 command_queue_(command_queue),
91 is_gpu_disabled_sync_switch_(
std::move(is_gpu_disabled_sync_switch)) {
98 sync_switch_observer_.reset(
new SyncSwitchObserver(*
this));
99 is_gpu_disabled_sync_switch_->AddObserver(sync_switch_observer_.get());
103 if (shader_libraries == nil) {
109 auto library = std::shared_ptr<ShaderLibraryMTL>(
110 new ShaderLibraryMTL(shader_libraries));
111 if (!library->IsValid()) {
115 shader_library_ = std::move(library);
121 std::shared_ptr<PipelineLibraryMTL>(
new PipelineLibraryMTL(device_));
127 std::shared_ptr<SamplerLibraryMTL>(
new SamplerLibraryMTL(device_));
132 resource_allocator_ = std::shared_ptr<AllocatorMTL>(
133 new AllocatorMTL(device_,
"Impeller Permanents Allocator"));
134 if (!resource_allocator_) {
140 device_capabilities_ =
142 ? pixel_format_override.value()
144 command_queue_ip_ = std::make_shared<CommandQueue>();
146 gpu_tracer_ = std::make_shared<GPUTracerMTL>();
147 capture_manager_ = std::make_shared<ImpellerMetalCaptureManager>(device_);
154 const std::vector<std::string>& libraries_paths) {
155 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
156 for (
const auto& library_path : libraries_paths) {
159 << library_path <<
"'";
162 NSError* shader_library_error = nil;
163 auto library = [
device newLibraryWithFile:@(library_path.c_str())
164 error:&shader_library_error];
166 FML_LOG(ERROR) <<
"Could not create shader library: "
167 << shader_library_error.localizedDescription.UTF8String;
170 [found_libraries addObject:library];
172 return found_libraries;
177 const std::vector<std::shared_ptr<fml::Mapping>>& libraries_data,
178 const std::string& label) {
179 NSMutableArray<id<MTLLibrary>>* found_libraries = [NSMutableArray array];
180 for (
const auto& library_data : libraries_data) {
181 if (library_data ==
nullptr) {
182 FML_LOG(ERROR) <<
"Shader library data was null.";
186 __block
auto data = library_data;
189 ::dispatch_data_create(library_data->GetMapping(),
190 library_data->GetSize(),
191 dispatch_get_main_queue(),
197 if (!dispatch_data) {
198 FML_LOG(ERROR) <<
"Could not wrap shader data in dispatch data.";
202 NSError* shader_library_error = nil;
203 auto library = [
device newLibraryWithData:dispatch_data
204 error:&shader_library_error];
206 FML_LOG(ERROR) <<
"Could not create shader library: "
207 << shader_library_error.localizedDescription.UTF8String;
210 if (!label.empty()) {
211 library.label = @(label.c_str());
213 [found_libraries addObject:library];
215 return found_libraries;
219 return ::MTLCreateSystemDefaultDevice();
223 auto command_queue =
device.newCommandQueue;
224 if (!command_queue) {
228 command_queue.label =
@"Impeller Command Queue";
229 return command_queue;
232std::shared_ptr<ContextMTL> ContextMTL::Create(
234 const std::vector<std::string>& shader_library_paths,
235 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
238 if (!command_queue) {
241 auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(
242 flags,
device, command_queue,
244 std::move(is_gpu_disabled_sync_switch)));
245 if (!context->IsValid()) {
246 FML_LOG(ERROR) <<
"Could not create Metal context.";
252std::shared_ptr<ContextMTL> ContextMTL::Create(
254 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
255 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
256 const std::string& library_label,
257 std::optional<PixelFormat> pixel_format_override) {
260 if (!command_queue) {
263 auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(
264 flags,
device, command_queue,
267 std::move(is_gpu_disabled_sync_switch), pixel_format_override));
268 if (!context->IsValid()) {
269 FML_LOG(ERROR) <<
"Could not create Metal context.";
275std::shared_ptr<ContextMTL> ContextMTL::Create(
278 id<MTLCommandQueue> command_queue,
279 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
280 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
281 const std::string& library_label) {
282 auto context = std::shared_ptr<ContextMTL>(
286 std::move(is_gpu_disabled_sync_switch)));
287 if (!context->IsValid()) {
288 FML_LOG(ERROR) <<
"Could not create Metal context.";
294ContextMTL::~ContextMTL() {
295 is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
299 return Context::BackendType::kMetal;
303std::string ContextMTL::DescribeGpuModel()
const {
304 return std::string([[device_
name] UTF8String]);
308bool ContextMTL::IsValid()
const {
313std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary()
const {
314 return shader_library_;
318std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary()
const {
319 return pipeline_library_;
323std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary()
const {
324 return sampler_library_;
328std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBuffer()
const {
329 return CreateCommandBufferInQueue(command_queue_);
333void ContextMTL::Shutdown() {}
336std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer()
const {
341std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
343 return is_gpu_disabled_sync_switch_;
346std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
347 id<MTLCommandQueue>
queue)
const {
352 auto buffer = std::shared_ptr<CommandBufferMTL>(
353 new CommandBufferMTL(weak_from_this(), device_,
queue));
354 if (!buffer->IsValid()) {
360std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator()
const {
361 return resource_allocator_;
364id<MTLDevice> ContextMTL::GetMTLDevice()
const {
368const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities()
const {
369 return device_capabilities_;
372void ContextMTL::SetCapabilities(
373 const std::shared_ptr<const Capabilities>& capabilities) {
374 device_capabilities_ = capabilities;
378bool ContextMTL::UpdateOffscreenLayerPixelFormat(
PixelFormat format) {
383id<MTLCommandBuffer> ContextMTL::CreateMTLCommandBuffer(
384 const std::string& label)
const {
385 auto buffer = [command_queue_ commandBuffer];
386 if (!label.empty()) {
387 [buffer setLabel:@(label.data())];
394 std::vector<PendingTasks> failed_tasks;
396 Lock lock(tasks_awaiting_gpu_mutex_);
397 tasks_awaiting_gpu_.push_back(PendingTasks{task, failure});
398 int32_t failed_task_count =
399 tasks_awaiting_gpu_.size() - kMaxTasksAwaitingGPU;
400 if (failed_task_count > 0) {
401 failed_tasks.reserve(failed_task_count);
402 failed_tasks.insert(failed_tasks.end(),
403 std::make_move_iterator(tasks_awaiting_gpu_.begin()),
404 std::make_move_iterator(tasks_awaiting_gpu_.begin() +
406 tasks_awaiting_gpu_.erase(
407 tasks_awaiting_gpu_.begin(),
408 tasks_awaiting_gpu_.begin() + failed_task_count);
411 for (
const PendingTasks& task : failed_tasks) {
418void ContextMTL::FlushTasksAwaitingGPU() {
419 std::deque<PendingTasks> tasks_awaiting_gpu;
421 Lock lock(tasks_awaiting_gpu_mutex_);
422 std::swap(tasks_awaiting_gpu, tasks_awaiting_gpu_);
424 std::vector<PendingTasks> tasks_to_queue;
425 for (
const auto& task : tasks_awaiting_gpu) {
437 tasks_to_queue.push_back(task);
440 if (!tasks_to_queue.empty()) {
441 Lock lock(tasks_awaiting_gpu_mutex_);
442 tasks_awaiting_gpu_.insert(tasks_awaiting_gpu_.end(),
443 tasks_to_queue.begin(), tasks_to_queue.end());
447ContextMTL::SyncSwitchObserver::SyncSwitchObserver(
ContextMTL& parent)
450void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(
bool new_is_disabled) {
451 if (!new_is_disabled) {
452 parent_.FlushTasksAwaitingGPU();
458 return command_queue_ip_;
467const std::shared_ptr<ImpellerMetalCaptureManager>
468ContextMTL::GetCaptureManager()
const {
469 return capture_manager_;
474 current_capture_scope_ = [[MTLCaptureManager sharedCaptureManager]
475 newCaptureScopeWithDevice:
device];
476 [current_capture_scope_ setLabel:
@"Impeller Frame"];
480 return scope_active_;
487 scope_active_ =
true;
488 [current_capture_scope_ beginScope];
493 [current_capture_scope_ endScope];
494 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 & 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.
const uint8_t uint32_t uint32_t GError ** error
uint32_t uint32_t * format
#define FML_LOG(severity)
#define FML_DCHECK(condition)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set profile Make the profiler discard new samples once the profiler sample buffer is full When this flag is not the profiler sample buffer is used as a ring buffer
bool IsFile(const std::string &path)
std::function< void()> closure
static bool DeviceSupportsExtendedRangeFormats(id< MTLDevice > device)
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFilePaths(id< MTLDevice > device, const std::vector< std::string > &libraries_paths)
static id< MTLCommandQueue > CreateMetalCommandQueue(id< MTLDevice > device)
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
static id< MTLDevice > CreateMetalDevice()
static NSArray< id< MTLLibrary > > * MTLShaderLibraryFromFileData(id< MTLDevice > device, const std::vector< std::shared_ptr< fml::Mapping > > &libraries_data, const std::string &label)
ISize DeviceMaxTextureSizeSupported(id< MTLDevice > device)
static bool DeviceSupportsComputeSubgroups(id< MTLDevice > device)
static bool DeviceSupportsFramebufferFetch(id< MTLDevice > device)
static std::unique_ptr< Capabilities > InferMetalCapabilities(id< MTLDevice > device, PixelFormat color_format)
Represents the 2 code paths available when calling |SyncSwitchExecute|.
Handlers & SetIfFalse(const std::function< void()> &handler)
Sets the handler that will be executed if the |SyncSwitch| is false.
std::shared_ptr< const fml::Mapping > data