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];
46 return [
device supportsFamily:MTLGPUFamilyApple3];
70#if FML_OS_IOS && !TARGET_OS_SIMULATOR
78ContextMTL::ContextMTL(
81 id<MTLCommandQueue> command_queue,
82 NSArray<id<MTLLibrary>>* shader_libraries,
83 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
84 std::optional<PixelFormat> pixel_format_override)
87 command_queue_(command_queue),
88 is_gpu_disabled_sync_switch_(
std::move(is_gpu_disabled_sync_switch)) {
95 sync_switch_observer_.reset(
new SyncSwitchObserver(*
this));
96 is_gpu_disabled_sync_switch_->AddObserver(sync_switch_observer_.get());
100 if (shader_libraries == nil) {
106 auto library = std::shared_ptr<ShaderLibraryMTL>(
107 new ShaderLibraryMTL(shader_libraries));
108 if (!library->IsValid()) {
112 shader_library_ = std::move(library);
118 std::shared_ptr<PipelineLibraryMTL>(
new PipelineLibraryMTL(device_));
124 std::shared_ptr<SamplerLibraryMTL>(
new SamplerLibraryMTL(device_));
129 resource_allocator_ = std::shared_ptr<AllocatorMTL>(
130 new AllocatorMTL(device_,
"Impeller Permanents Allocator"));
131 if (!resource_allocator_) {
137 device_capabilities_ =
139 ? pixel_format_override.value()
141 command_queue_ip_ = std::make_shared<CommandQueue>();
143 gpu_tracer_ = std::make_shared<GPUTracerMTL>();
144 capture_manager_ = std::make_shared<ImpellerMetalCaptureManager>(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) {
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;
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;
229std::shared_ptr<ContextMTL> ContextMTL::Create(
231 const std::vector<std::string>& shader_library_paths,
232 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch) {
235 if (!command_queue) {
238 auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(
239 flags,
device, command_queue,
241 std::move(is_gpu_disabled_sync_switch)));
242 if (!context->IsValid()) {
243 FML_LOG(ERROR) <<
"Could not create Metal context.";
249std::shared_ptr<ContextMTL> ContextMTL::Create(
251 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
252 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
253 const std::string& library_label,
254 std::optional<PixelFormat> pixel_format_override) {
257 if (!command_queue) {
260 auto context = std::shared_ptr<ContextMTL>(
new ContextMTL(
261 flags,
device, command_queue,
264 std::move(is_gpu_disabled_sync_switch), pixel_format_override));
265 if (!context->IsValid()) {
266 FML_LOG(ERROR) <<
"Could not create Metal context.";
272std::shared_ptr<ContextMTL> ContextMTL::Create(
275 id<MTLCommandQueue> command_queue,
276 const std::vector<std::shared_ptr<fml::Mapping>>& shader_libraries_data,
277 std::shared_ptr<const fml::SyncSwitch> is_gpu_disabled_sync_switch,
278 const std::string& library_label) {
279 auto context = std::shared_ptr<ContextMTL>(
283 std::move(is_gpu_disabled_sync_switch)));
284 if (!context->IsValid()) {
285 FML_LOG(ERROR) <<
"Could not create Metal context.";
291ContextMTL::~ContextMTL() {
292 is_gpu_disabled_sync_switch_->RemoveObserver(sync_switch_observer_.get());
296 return Context::BackendType::kMetal;
300std::string ContextMTL::DescribeGpuModel()
const {
301 return std::string([[device_
name] UTF8String]);
305bool ContextMTL::IsValid()
const {
310std::shared_ptr<ShaderLibrary> ContextMTL::GetShaderLibrary()
const {
311 return shader_library_;
315std::shared_ptr<PipelineLibrary> ContextMTL::GetPipelineLibrary()
const {
316 return pipeline_library_;
320std::shared_ptr<SamplerLibrary> ContextMTL::GetSamplerLibrary()
const {
321 return sampler_library_;
325std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBuffer()
const {
326 return CreateCommandBufferInQueue(command_queue_);
330void ContextMTL::Shutdown() {}
333std::shared_ptr<GPUTracerMTL> ContextMTL::GetGPUTracer()
const {
338std::shared_ptr<const fml::SyncSwitch> ContextMTL::GetIsGpuDisabledSyncSwitch()
340 return is_gpu_disabled_sync_switch_;
343std::shared_ptr<CommandBuffer> ContextMTL::CreateCommandBufferInQueue(
344 id<MTLCommandQueue>
queue)
const {
349 auto buffer = std::shared_ptr<CommandBufferMTL>(
350 new CommandBufferMTL(weak_from_this(), device_,
queue));
351 if (!buffer->IsValid()) {
357std::shared_ptr<Allocator> ContextMTL::GetResourceAllocator()
const {
358 return resource_allocator_;
361id<MTLDevice> ContextMTL::GetMTLDevice()
const {
365const std::shared_ptr<const Capabilities>& ContextMTL::GetCapabilities()
const {
366 return device_capabilities_;
369void ContextMTL::SetCapabilities(
370 const std::shared_ptr<const Capabilities>& capabilities) {
371 device_capabilities_ = capabilities;
375bool ContextMTL::UpdateOffscreenLayerPixelFormat(
PixelFormat format) {
380id<MTLCommandBuffer> ContextMTL::CreateMTLCommandBuffer(
381 const std::string& label)
const {
382 auto buffer = [command_queue_ commandBuffer];
383 if (!label.empty()) {
384 [buffer setLabel:@(label.data())];
391 std::vector<PendingTasks> failed_tasks;
393 Lock lock(tasks_awaiting_gpu_mutex_);
394 tasks_awaiting_gpu_.push_back(PendingTasks{task, failure});
395 int32_t failed_task_count =
396 tasks_awaiting_gpu_.size() - kMaxTasksAwaitingGPU;
397 if (failed_task_count > 0) {
398 failed_tasks.reserve(failed_task_count);
399 failed_tasks.insert(failed_tasks.end(),
400 std::make_move_iterator(tasks_awaiting_gpu_.begin()),
401 std::make_move_iterator(tasks_awaiting_gpu_.begin() +
403 tasks_awaiting_gpu_.erase(
404 tasks_awaiting_gpu_.begin(),
405 tasks_awaiting_gpu_.begin() + failed_task_count);
408 for (
const PendingTasks& task : failed_tasks) {
415void ContextMTL::FlushTasksAwaitingGPU() {
416 std::deque<PendingTasks> tasks_awaiting_gpu;
418 Lock lock(tasks_awaiting_gpu_mutex_);
419 std::swap(tasks_awaiting_gpu, tasks_awaiting_gpu_);
421 std::vector<PendingTasks> tasks_to_queue;
422 for (
const auto& task : tasks_awaiting_gpu) {
434 tasks_to_queue.push_back(task);
437 if (!tasks_to_queue.empty()) {
438 Lock lock(tasks_awaiting_gpu_mutex_);
439 tasks_awaiting_gpu_.insert(tasks_awaiting_gpu_.end(),
440 tasks_to_queue.begin(), tasks_to_queue.end());
444ContextMTL::SyncSwitchObserver::SyncSwitchObserver(
ContextMTL& parent)
447void ContextMTL::SyncSwitchObserver::OnSyncSwitchUpdate(
bool new_is_disabled) {
448 if (!new_is_disabled) {
449 parent_.FlushTasksAwaitingGPU();
455 return command_queue_ip_;
464const std::shared_ptr<ImpellerMetalCaptureManager>
465ContextMTL::GetCaptureManager()
const {
466 return capture_manager_;
471 current_capture_scope_ = [[MTLCaptureManager sharedCaptureManager]
472 newCaptureScopeWithDevice:
device];
473 [current_capture_scope_ setLabel:
@"Impeller Frame"];
477 return scope_active_;
484 scope_active_ =
true;
485 [current_capture_scope_ beginScope];
490 [current_capture_scope_ endScope];
491 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