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(const std::string& label) const {
144  if (label.empty()) {
145  return;
146  }
147 
148  [buffer_ setLabel:@(label.data())];
149 }
150 
151 static CommandBuffer::Status ToCommitResult(MTLCommandBufferStatus status) {
152  switch (status) {
153  case MTLCommandBufferStatusCompleted:
154  return CommandBufferMTL::Status::kCompleted;
155  case MTLCommandBufferStatusEnqueued:
156  return CommandBufferMTL::Status::kPending;
157  default:
158  break;
159  }
160  return CommandBufferMTL::Status::kError;
161 }
162 
163 bool CommandBufferMTL::OnSubmitCommands(CompletionCallback callback) {
164  auto context = context_.lock();
165  if (!context) {
166  return false;
167  }
168 #ifdef IMPELLER_DEBUG
169  ContextMTL::Cast(*context).GetGPUTracer()->RecordCmdBuffer(buffer_);
170 #endif // IMPELLER_DEBUG
171  if (callback) {
172  [buffer_
173  addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
174  [[maybe_unused]] auto result =
176  FML_DCHECK(result)
177  << "Must not have errors during command buffer submission.";
178  callback(ToCommitResult(buffer.status));
179  }];
180  }
181 
182  [buffer_ commit];
183 
184  buffer_ = nil;
185  return true;
186 }
187 
188 void CommandBufferMTL::OnWaitUntilScheduled() {}
189 
190 std::shared_ptr<RenderPass> CommandBufferMTL::OnCreateRenderPass(
191  RenderTarget target) {
192  if (!buffer_) {
193  return nullptr;
194  }
195 
196  auto context = context_.lock();
197  if (!context) {
198  return nullptr;
199  }
200  auto pass = std::shared_ptr<RenderPassMTL>(
201  new RenderPassMTL(context, target, buffer_));
202  if (!pass->IsValid()) {
203  return nullptr;
204  }
205 
206  return pass;
207 }
208 
209 std::shared_ptr<BlitPass> CommandBufferMTL::OnCreateBlitPass() {
210  if (!buffer_) {
211  return nullptr;
212  }
213 
214  auto pass = std::shared_ptr<BlitPassMTL>(new BlitPassMTL(buffer_, device_));
215  if (!pass->IsValid()) {
216  return nullptr;
217  }
218 
219  return pass;
220 }
221 
222 std::shared_ptr<ComputePass> CommandBufferMTL::OnCreateComputePass() {
223  if (!buffer_) {
224  return nullptr;
225  }
226  auto context = context_.lock();
227  if (!context) {
228  return nullptr;
229  }
230 
231  auto pass =
232  std::shared_ptr<ComputePassMTL>(new ComputePassMTL(context, buffer_));
233  if (!pass->IsValid()) {
234  return nullptr;
235  }
236 
237  return pass;
238 }
239 
240 } // 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:151
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:91
compute_pass_mtl.h
impeller::CommandBuffer::Status
Status
Definition: command_buffer.h:49
impeller
Definition: allocation.cc:12