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<MTLDevice> device,
132  id<MTLCommandQueue> queue)
133  : CommandBuffer(context),
134  buffer_(CreateCommandBuffer(queue)),
135  device_(device) {}
136 
137 CommandBufferMTL::~CommandBufferMTL() = default;
138 
139 bool CommandBufferMTL::IsValid() const {
140  return buffer_ != nil;
141 }
142 
143 void CommandBufferMTL::SetLabel(std::string_view label) const {
144 #ifdef IMPELLER_DEBUG
145  if (label.empty()) {
146  return;
147  }
148 
149  [buffer_ setLabel:@(label.data())];
150 #endif // IMPELLER_DEBUG
151 }
152 
153 static CommandBuffer::Status ToCommitResult(MTLCommandBufferStatus status) {
154  switch (status) {
155  case MTLCommandBufferStatusCompleted:
156  return CommandBufferMTL::Status::kCompleted;
157  case MTLCommandBufferStatusEnqueued:
158  return CommandBufferMTL::Status::kPending;
159  default:
160  break;
161  }
162  return CommandBufferMTL::Status::kError;
163 }
164 
165 bool CommandBufferMTL::OnSubmitCommands(CompletionCallback callback) {
166  auto context = context_.lock();
167  if (!context) {
168  return false;
169  }
170 #ifdef IMPELLER_DEBUG
171  ContextMTL::Cast(*context).GetGPUTracer()->RecordCmdBuffer(buffer_);
172 #endif // IMPELLER_DEBUG
173  if (callback) {
174  [buffer_
175  addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
176  [[maybe_unused]] auto result =
178  FML_DCHECK(result)
179  << "Must not have errors during command buffer submission.";
180  callback(ToCommitResult(buffer.status));
181  }];
182  }
183 
184  [buffer_ commit];
185 
186  buffer_ = nil;
187  return true;
188 }
189 
190 void CommandBufferMTL::OnWaitUntilCompleted() {}
191 
192 void CommandBufferMTL::OnWaitUntilScheduled() {}
193 
194 std::shared_ptr<RenderPass> CommandBufferMTL::OnCreateRenderPass(
195  RenderTarget target) {
196  if (!buffer_) {
197  return nullptr;
198  }
199 
200  auto context = context_.lock();
201  if (!context) {
202  return nullptr;
203  }
204  auto pass = std::shared_ptr<RenderPassMTL>(
205  new RenderPassMTL(context, target, buffer_));
206  if (!pass->IsValid()) {
207  return nullptr;
208  }
209 
210  return pass;
211 }
212 
213 std::shared_ptr<BlitPass> CommandBufferMTL::OnCreateBlitPass() {
214  if (!buffer_) {
215  return nullptr;
216  }
217 
218  auto pass = std::shared_ptr<BlitPassMTL>(new BlitPassMTL(buffer_, device_));
219  if (!pass->IsValid()) {
220  return nullptr;
221  }
222 
223  return pass;
224 }
225 
226 std::shared_ptr<ComputePass> CommandBufferMTL::OnCreateComputePass() {
227  if (!buffer_) {
228  return nullptr;
229  }
230  auto context = context_.lock();
231  if (!context) {
232  return nullptr;
233  }
234 
235  auto pass =
236  std::shared_ptr<ComputePassMTL>(new ComputePassMTL(context, buffer_));
237  if (!pass->IsValid()) {
238  return nullptr;
239  }
240 
241  return pass;
242 }
243 
244 } // namespace impeller
static bool LogMTLCommandBufferErrorIfPresent(id< MTLCommandBuffer > buffer)
API_AVAILABLE(ios(14.0), macos(11.0)) static NSString *MTLCommandEncoderErrorStateToString(MTLCommandEncoderErrorState state)
static id< MTLCommandBuffer > CreateCommandBuffer(id< MTLCommandQueue > queue)
static NSString * MTLCommandBufferErrorToString(MTLCommandBufferError code)
static CommandBuffer::Status ToCommitResult(MTLCommandBufferStatus status)
#define VALIDATION_LOG
Definition: validation.h:91