Flutter Impeller
command_buffer_mtl.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
7 #include "flutter/fml/make_copyable.h"
8 #include "flutter/fml/synchronization/semaphore.h"
9 
14 
15 namespace impeller {
16 
17 API_AVAILABLE(ios(14.0), macos(11.0))
18 static NSString* MTLCommandEncoderErrorStateToString(
19  MTLCommandEncoderErrorState state) {
20  switch (state) {
21  case MTLCommandEncoderErrorStateUnknown:
22  return @"unknown";
23  case MTLCommandEncoderErrorStateCompleted:
24  return @"completed";
25  case MTLCommandEncoderErrorStateAffected:
26  return @"affected";
27  case MTLCommandEncoderErrorStatePending:
28  return @"pending";
29  case MTLCommandEncoderErrorStateFaulted:
30  return @"faulted";
31  }
32  return @"unknown";
33 }
34 
35 static NSString* MTLCommandBufferErrorToString(MTLCommandBufferError code) {
36  switch (code) {
37  case MTLCommandBufferErrorNone:
38  return @"none";
39  case MTLCommandBufferErrorInternal:
40  return @"internal";
41  case MTLCommandBufferErrorTimeout:
42  return @"timeout";
43  case MTLCommandBufferErrorPageFault:
44  return @"page fault";
45  case MTLCommandBufferErrorNotPermitted:
46  return @"not permitted";
47  case MTLCommandBufferErrorOutOfMemory:
48  return @"out of memory";
49  case MTLCommandBufferErrorInvalidResource:
50  return @"invalid resource";
51  case MTLCommandBufferErrorMemoryless:
52  return @"memory-less";
53  default:
54  break;
55  }
56 
57  return [NSString stringWithFormat:@"<unknown> %zu", code];
58 }
59 
60 static bool LogMTLCommandBufferErrorIfPresent(id<MTLCommandBuffer> buffer) {
61  if (!buffer) {
62  return true;
63  }
64 
65  if (buffer.status == MTLCommandBufferStatusCompleted) {
66  return true;
67  }
68 
69  std::stringstream stream;
70  stream << ">>>>>>>" << std::endl;
71  stream << "Impeller command buffer could not be committed!" << std::endl;
72 
73  if (auto desc = buffer.error.localizedDescription) {
74  stream << desc.UTF8String << std::endl;
75  }
76 
77  if (buffer.error) {
78  stream << "Domain: "
79  << (buffer.error.domain.length > 0u ? buffer.error.domain.UTF8String
80  : "<unknown>")
81  << " Code: "
83  static_cast<MTLCommandBufferError>(buffer.error.code))
84  .UTF8String
85  << std::endl;
86  }
87 
88  if (@available(iOS 14.0, macOS 11.0, *)) {
89  NSArray<id<MTLCommandBufferEncoderInfo>>* infos =
90  buffer.error.userInfo[MTLCommandBufferEncoderInfoErrorKey];
91  for (id<MTLCommandBufferEncoderInfo> info in infos) {
92  stream << (info.label.length > 0u ? info.label.UTF8String
93  : "<Unlabelled Render Pass>")
94  << ": "
95  << MTLCommandEncoderErrorStateToString(info.errorState).UTF8String
96  << std::endl;
97 
98  auto signposts = [info.debugSignposts componentsJoinedByString:@", "];
99  if (signposts.length > 0u) {
100  stream << signposts.UTF8String << std::endl;
101  }
102  }
103 
104  for (id<MTLFunctionLog> log in buffer.logs) {
105  auto desc = log.description;
106  if (desc.length > 0u) {
107  stream << desc.UTF8String << std::endl;
108  }
109  }
110  }
111 
112  stream << "<<<<<<<";
113  VALIDATION_LOG << stream.str();
114  return false;
115 }
116 
117 static id<MTLCommandBuffer> CreateCommandBuffer(id<MTLCommandQueue> queue) {
118 #ifndef FLUTTER_RELEASE
119  if (@available(iOS 14.0, macOS 11.0, *)) {
120  auto desc = [[MTLCommandBufferDescriptor alloc] init];
121  // Degrades CPU performance slightly but is well worth the cost for typical
122  // Impeller workloads.
123  desc.errorOptions = MTLCommandBufferErrorOptionEncoderExecutionStatus;
124  return [queue commandBufferWithDescriptor:desc];
125  }
126 #endif // FLUTTER_RELEASE
127  return [queue commandBuffer];
128 }
129 
130 CommandBufferMTL::CommandBufferMTL(const std::weak_ptr<const Context>& context,
131  id<MTLCommandQueue> queue)
132  : CommandBuffer(context), buffer_(CreateCommandBuffer(queue)) {}
133 
134 CommandBufferMTL::~CommandBufferMTL() = default;
135 
136 bool CommandBufferMTL::IsValid() const {
137  return buffer_ != nil;
138 }
139 
140 void CommandBufferMTL::SetLabel(const std::string& label) const {
141  if (label.empty()) {
142  return;
143  }
144 
145  [buffer_ setLabel:@(label.data())];
146 }
147 
148 static CommandBuffer::Status ToCommitResult(MTLCommandBufferStatus status) {
149  switch (status) {
150  case MTLCommandBufferStatusCompleted:
151  return CommandBufferMTL::Status::kCompleted;
152  case MTLCommandBufferStatusEnqueued:
153  return CommandBufferMTL::Status::kPending;
154  default:
155  break;
156  }
157  return CommandBufferMTL::Status::kError;
158 }
159 
160 bool CommandBufferMTL::OnSubmitCommands(CompletionCallback callback) {
161  auto context = context_.lock();
162  if (!context) {
163  return false;
164  }
165 #ifdef IMPELLER_DEBUG
166  ContextMTL::Cast(*context).GetGPUTracer()->RecordCmdBuffer(buffer_);
167 #endif // IMPELLER_DEBUG
168  if (callback) {
169  [buffer_
170  addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
171  [[maybe_unused]] auto result =
173  FML_DCHECK(result)
174  << "Must not have errors during command buffer submission.";
175  callback(ToCommitResult(buffer.status));
176  }];
177  }
178 
179  [buffer_ commit];
180 
181  buffer_ = nil;
182  return true;
183 }
184 
185 void CommandBufferMTL::OnWaitUntilScheduled() {}
186 
187 std::shared_ptr<RenderPass> CommandBufferMTL::OnCreateRenderPass(
188  RenderTarget target) {
189  if (!buffer_) {
190  return nullptr;
191  }
192 
193  auto context = context_.lock();
194  if (!context) {
195  return nullptr;
196  }
197  auto pass = std::shared_ptr<RenderPassMTL>(
198  new RenderPassMTL(context, target, buffer_));
199  if (!pass->IsValid()) {
200  return nullptr;
201  }
202 
203  return pass;
204 }
205 
206 std::shared_ptr<BlitPass> CommandBufferMTL::OnCreateBlitPass() {
207  if (!buffer_) {
208  return nullptr;
209  }
210 
211  auto pass = std::shared_ptr<BlitPassMTL>(new BlitPassMTL(buffer_));
212  if (!pass->IsValid()) {
213  return nullptr;
214  }
215 
216  return pass;
217 }
218 
219 std::shared_ptr<ComputePass> CommandBufferMTL::OnCreateComputePass() {
220  if (!buffer_) {
221  return nullptr;
222  }
223  auto context = context_.lock();
224  if (!context) {
225  return nullptr;
226  }
227 
228  auto pass =
229  std::shared_ptr<ComputePassMTL>(new ComputePassMTL(context, buffer_));
230  if (!pass->IsValid()) {
231  return nullptr;
232  }
233 
234  return pass;
235 }
236 
237 } // namespace impeller
command_buffer_mtl.h
context_mtl.h
render_pass_mtl.h
impeller::CreateCommandBuffer
static id< MTLCommandBuffer > CreateCommandBuffer(id< MTLCommandQueue > queue)
Definition: command_buffer_mtl.mm:117
impeller::LogMTLCommandBufferErrorIfPresent
static bool LogMTLCommandBufferErrorIfPresent(id< MTLCommandBuffer > buffer)
Definition: command_buffer_mtl.mm:60
impeller::MTLCommandBufferErrorToString
static NSString * MTLCommandBufferErrorToString(MTLCommandBufferError code)
Definition: command_buffer_mtl.mm:35
blit_pass_mtl.h
impeller::ToCommitResult
static CommandBuffer::Status ToCommitResult(MTLCommandBufferStatus status)
Definition: command_buffer_mtl.mm:148
impeller::API_AVAILABLE
API_AVAILABLE(ios(14.0), macos(11.0)) static NSString *MTLCommandEncoderErrorStateToString(MTLCommandEncoderErrorState state)
Definition: command_buffer_mtl.mm:17
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:73
compute_pass_mtl.h
impeller::CommandBuffer::Status
Status
Definition: command_buffer.h:49
impeller
Definition: aiks_blur_unittests.cc:20