Flutter Impeller
compute_subgroup_unittests.cc
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 
5 #include <future>
6 #include <numeric>
7 
8 #include "compute_tessellator.h"
9 #include "flutter/fml/synchronization/waitable_event.h"
10 #include "flutter/fml/time/time_point.h"
11 #include "flutter/testing/testing.h"
12 #include "gmock/gmock.h"
13 #include "impeller/base/strings.h"
14 #include "impeller/core/formats.h"
19 #include "impeller/fixtures/sample.comp.h"
20 #include "impeller/fixtures/stage1.comp.h"
21 #include "impeller/fixtures/stage2.comp.h"
22 #include "impeller/geometry/path.h"
29 #include "impeller/renderer/path_polyline.comp.h"
32 #include "impeller/renderer/stroke.comp.h"
33 #include "third_party/imgui/imgui.h"
34 #include "third_party/skia/include/utils/SkParsePath.h"
35 
36 namespace impeller {
37 namespace testing {
38 
41 
42 TEST_P(ComputeSubgroupTest, CapabilitiesSuportSubgroups) {
43  auto context = GetContext();
44  ASSERT_TRUE(context);
45  ASSERT_TRUE(context->GetCapabilities()->SupportsComputeSubgroups());
46 }
47 
48 TEST_P(ComputeSubgroupTest, PathPlayground) {
49  // Renders stroked SVG paths in an interactive playground.
50  using SS = StrokeComputeShader;
51 
52  auto context = GetContext();
53  ASSERT_TRUE(context);
54  ASSERT_TRUE(context->GetCapabilities()->SupportsComputeSubgroups());
55  char svg_path_data[16384] =
56  "M140 20 "
57  "C73 20 20 74 20 140 "
58  "c0 135 136 170 228 303 "
59  "88-132 229-173 229-303 "
60  "0-66-54-120-120-120 "
61  "-48 0-90 28-109 69 "
62  "-19-41-60-69-108-69z";
63  size_t vertex_count = 0;
64  Scalar stroke_width = 1.0;
65 
66  auto vertex_buffer = CreateHostVisibleDeviceBuffer<SS::VertexBuffer<2048>>(
67  context, "VertexBuffer");
68  auto vertex_buffer_count =
69  CreateHostVisibleDeviceBuffer<SS::VertexBufferCount>(context,
70  "VertexCount");
71 
72  auto callback = [&](RenderPass& pass) -> bool {
73  ::memset(vertex_buffer_count->OnGetContents(), 0,
74  sizeof(SS::VertexBufferCount));
75  ::memset(vertex_buffer->OnGetContents(), 0, sizeof(SS::VertexBuffer<2048>));
76  const auto* main_viewport = ImGui::GetMainViewport();
77  ImGui::SetNextWindowPos(
78  ImVec2(main_viewport->WorkPos.x + 650, main_viewport->WorkPos.y + 20));
79  ImGui::Begin("Path data", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
80  ImGui::InputTextMultiline("Path", svg_path_data,
81  IM_ARRAYSIZE(svg_path_data));
82  ImGui::DragFloat("Stroke width", &stroke_width, .1, 0.0, 25.0);
83 
84  SkPath sk_path;
85  if (SkParsePath::FromSVGString(svg_path_data, &sk_path)) {
86  std::promise<bool> promise;
87  auto future = promise.get_future();
88 
89  auto path = skia_conversions::ToPath(sk_path);
90  auto host_buffer = HostBuffer::Create(context->GetResourceAllocator());
91  auto status =
94  .Tessellate(path, *host_buffer, context,
95  DeviceBuffer::AsBufferView(vertex_buffer),
96  DeviceBuffer::AsBufferView(vertex_buffer_count),
97  [vertex_buffer_count, &vertex_count,
98  &promise](CommandBuffer::Status status) {
99  vertex_count =
100  reinterpret_cast<SS::VertexBufferCount*>(
101  vertex_buffer_count->OnGetContents())
102  ->count;
103  promise.set_value(
105  });
106  switch (status) {
108  ImGui::Text("Failed to submit compute job (invalid command)");
109  break;
111  ImGui::Text("Failed to submit compute job (too many components) ");
112  break;
114  break;
115  }
116  if (!future.get()) {
117  ImGui::Text("Failed to submit compute job.");
118  return false;
119  }
120  if (vertex_count > 0) {
121  ImGui::Text("Vertex count: %zu", vertex_count);
122  }
123  } else {
124  ImGui::Text("Failed to parse path data");
125  }
126  ImGui::End();
127 
128  ContentContext renderer(context, nullptr);
129  if (!renderer.IsValid()) {
130  return false;
131  }
132 
134 
135  pass.SetCommandLabel("Draw Stroke");
136  pass.SetStencilReference(0);
137 
138  ContentContextOptions options;
139  options.sample_count = pass.GetRenderTarget().GetSampleCount();
141  pass.GetRenderTarget().GetRenderTargetPixelFormat();
143  pass.GetRenderTarget().GetStencilAttachment().has_value();
146 
147  options.stencil_mode =
149 
150  pass.SetPipeline(renderer.GetSolidFillPipeline(options));
151 
152  auto count = reinterpret_cast<SS::VertexBufferCount*>(
153  vertex_buffer_count->OnGetContents())
154  ->count;
155 
156  pass.SetVertexBuffer(
158  .vertex_count = count,
159  .index_type = IndexType::kNone});
160 
161  VS::FrameInfo frame_info;
162  auto world_matrix = Matrix::MakeScale(GetContentScale());
163  frame_info.mvp = pass.GetOrthographicTransform() * world_matrix;
164  frame_info.color = Color::Red().Premultiply();
165  VS::BindFrameInfo(
166  pass, renderer.GetTransientsBuffer().EmplaceUniform(frame_info));
167 
168  return pass.Draw().ok();
169  };
170  ASSERT_TRUE(OpenPlaygroundHere(callback));
171 }
172 
174  // The path in here is large enough to highlight issues around exceeding
175  // subgroup size.
176  using SS = StrokeComputeShader;
177 
178  auto context = GetContext();
179  ASSERT_TRUE(context);
180  ASSERT_TRUE(context->GetCapabilities()->SupportsComputeSubgroups());
181  size_t vertex_count = 0;
182  Scalar stroke_width = 1.0;
183 
184  auto vertex_buffer = CreateHostVisibleDeviceBuffer<SS::VertexBuffer<2048>>(
185  context, "VertexBuffer");
186  auto vertex_buffer_count =
187  CreateHostVisibleDeviceBuffer<SS::VertexBufferCount>(context,
188  "VertexCount");
189 
190  auto complex_path =
191  PathBuilder{}
192  .MoveTo({359.934, 96.6335})
193  .CubicCurveTo({358.189, 96.7055}, {356.436, 96.7908},
194  {354.673, 96.8895})
195  .CubicCurveTo({354.571, 96.8953}, {354.469, 96.9016},
196  {354.367, 96.9075})
197  .CubicCurveTo({352.672, 97.0038}, {350.969, 97.113},
198  {349.259, 97.2355})
199  .CubicCurveTo({349.048, 97.2506}, {348.836, 97.2678},
200  {348.625, 97.2834})
201  .CubicCurveTo({347.019, 97.4014}, {345.407, 97.5299},
202  {343.789, 97.6722})
203  .CubicCurveTo({343.428, 97.704}, {343.065, 97.7402},
204  {342.703, 97.7734})
205  .CubicCurveTo({341.221, 97.9086}, {339.736, 98.0505},
206  {338.246, 98.207})
207  .CubicCurveTo({337.702, 98.2642}, {337.156, 98.3292},
208  {336.612, 98.3894})
209  .CubicCurveTo({335.284, 98.5356}, {333.956, 98.6837},
210  {332.623, 98.8476})
211  .CubicCurveTo({332.495, 98.8635}, {332.366, 98.8818},
212  {332.237, 98.8982})
213  .LineTo({332.237, 102.601})
214  .LineTo({321.778, 102.601})
215  .LineTo({321.778, 100.382})
216  .CubicCurveTo({321.572, 100.413}, {321.367, 100.442},
217  {321.161, 100.476})
218  .CubicCurveTo({319.22, 100.79}, {317.277, 101.123},
219  {315.332, 101.479})
220  .CubicCurveTo({315.322, 101.481}, {315.311, 101.482},
221  {315.301, 101.484})
222  .LineTo({310.017, 105.94})
223  .LineTo({309.779, 105.427})
224  .LineTo({314.403, 101.651})
225  .CubicCurveTo({314.391, 101.653}, {314.379, 101.656},
226  {314.368, 101.658})
227  .CubicCurveTo({312.528, 102.001}, {310.687, 102.366},
228  {308.846, 102.748})
229  .CubicCurveTo({307.85, 102.955}, {306.855, 103.182}, {305.859, 103.4})
230  .CubicCurveTo({305.048, 103.579}, {304.236, 103.75},
231  {303.425, 103.936})
232  .LineTo({299.105, 107.578})
233  .LineTo({298.867, 107.065})
234  .LineTo({302.394, 104.185})
235  .LineTo({302.412, 104.171})
236  .CubicCurveTo({301.388, 104.409}, {300.366, 104.67},
237  {299.344, 104.921})
238  .CubicCurveTo({298.618, 105.1}, {297.89, 105.269}, {297.165, 105.455})
239  .CubicCurveTo({295.262, 105.94}, {293.36, 106.445},
240  {291.462, 106.979})
241  .CubicCurveTo({291.132, 107.072}, {290.802, 107.163},
242  {290.471, 107.257})
243  .CubicCurveTo({289.463, 107.544}, {288.455, 107.839},
244  {287.449, 108.139})
245  .CubicCurveTo({286.476, 108.431}, {285.506, 108.73},
246  {284.536, 109.035})
247  .CubicCurveTo({283.674, 109.304}, {282.812, 109.579},
248  {281.952, 109.859})
249  .CubicCurveTo({281.177, 110.112}, {280.406, 110.377},
250  {279.633, 110.638})
251  .CubicCurveTo({278.458, 111.037}, {277.256, 111.449},
252  {276.803, 111.607})
253  .CubicCurveTo({276.76, 111.622}, {276.716, 111.637},
254  {276.672, 111.653})
255  .CubicCurveTo({275.017, 112.239}, {273.365, 112.836},
256  {271.721, 113.463})
257  .LineTo({271.717, 113.449})
258  .CubicCurveTo({271.496, 113.496}, {271.238, 113.559},
259  {270.963, 113.628})
260  .CubicCurveTo({270.893, 113.645}, {270.822, 113.663},
261  {270.748, 113.682})
262  .CubicCurveTo({270.468, 113.755}, {270.169, 113.834},
263  {269.839, 113.926})
264  .CubicCurveTo({269.789, 113.94}, {269.732, 113.957},
265  {269.681, 113.972})
266  .CubicCurveTo({269.391, 114.053}, {269.081, 114.143},
267  {268.756, 114.239})
268  .CubicCurveTo({268.628, 114.276}, {268.5, 114.314},
269  {268.367, 114.354})
270  .CubicCurveTo({268.172, 114.412}, {267.959, 114.478},
271  {267.752, 114.54})
272  .CubicCurveTo({263.349, 115.964}, {258.058, 117.695},
273  {253.564, 119.252})
274  .CubicCurveTo({253.556, 119.255}, {253.547, 119.258},
275  {253.538, 119.261})
276  .CubicCurveTo({251.844, 119.849}, {250.056, 120.474},
277  {248.189, 121.131})
278  .CubicCurveTo({248, 121.197}, {247.812, 121.264}, {247.621, 121.331})
279  .CubicCurveTo({247.079, 121.522}, {246.531, 121.715},
280  {245.975, 121.912})
281  .CubicCurveTo({245.554, 122.06}, {245.126, 122.212},
282  {244.698, 122.364})
283  .CubicCurveTo({244.071, 122.586}, {243.437, 122.811},
284  {242.794, 123.04})
285  .CubicCurveTo({242.189, 123.255}, {241.58, 123.472},
286  {240.961, 123.693})
287  .CubicCurveTo({240.659, 123.801}, {240.357, 123.909},
288  {240.052, 124.018})
289  .CubicCurveTo({239.12, 124.351}, {238.18, 124.687}, {237.22, 125.032})
290  .LineTo({237.164, 125.003})
291  .CubicCurveTo({236.709, 125.184}, {236.262, 125.358},
292  {235.81, 125.538})
293  .CubicCurveTo({235.413, 125.68}, {234.994, 125.832},
294  {234.592, 125.977})
295  .CubicCurveTo({234.592, 125.977}, {234.591, 125.977},
296  {234.59, 125.977})
297  .CubicCurveTo({222.206, 130.435}, {207.708, 135.753},
298  {192.381, 141.429})
299  .CubicCurveTo({162.77, 151.336}, {122.17, 156.894}, {84.1123, 160})
300  .LineTo({360, 160})
301  .LineTo({360, 119.256})
302  .LineTo({360, 106.332})
303  .LineTo({360, 96.6307})
304  .CubicCurveTo({359.978, 96.6317}, {359.956, 96.6326},
305  {359.934, 96.6335})
306  .Close()
307  .TakePath();
308 
309  auto callback = [&](RenderPass& pass) -> bool {
310  ::memset(vertex_buffer_count->OnGetContents(), 0,
311  sizeof(SS::VertexBufferCount));
312  ::memset(vertex_buffer->OnGetContents(), 0, sizeof(SS::VertexBuffer<2048>));
313 
314  ContentContext renderer(context, nullptr);
315  if (!renderer.IsValid()) {
316  return false;
317  }
318 
321  .Tessellate(
322  complex_path, renderer.GetTransientsBuffer(), context,
323  DeviceBuffer::AsBufferView(vertex_buffer),
324  DeviceBuffer::AsBufferView(vertex_buffer_count),
325  [vertex_buffer_count, &vertex_count](CommandBuffer::Status status) {
326  vertex_count = reinterpret_cast<SS::VertexBufferCount*>(
327  vertex_buffer_count->OnGetContents())
328  ->count;
329  });
330 
332 
333  pass.SetCommandLabel("Draw Stroke");
334  pass.SetStencilReference(0);
335 
336  ContentContextOptions options;
337  options.sample_count = pass.GetRenderTarget().GetSampleCount();
339  pass.GetRenderTarget().GetRenderTargetPixelFormat();
341  pass.GetRenderTarget().GetStencilAttachment().has_value();
344 
345  options.stencil_mode =
347 
348  pass.SetPipeline(renderer.GetSolidFillPipeline(options));
349 
350  auto count = reinterpret_cast<SS::VertexBufferCount*>(
351  vertex_buffer_count->OnGetContents())
352  ->count;
353 
354  pass.SetVertexBuffer(
356  .vertex_count = count,
357  .index_type = IndexType::kNone});
358 
359  VS::FrameInfo frame_info;
360  auto world_matrix = Matrix::MakeScale(GetContentScale());
361  frame_info.mvp = pass.GetOrthographicTransform() * world_matrix;
362  frame_info.color = Color::Red().Premultiply();
363  VS::BindFrameInfo(
364  pass, renderer.GetTransientsBuffer().EmplaceUniform(frame_info));
365 
366  return pass.Draw().ok();
367  };
368  ASSERT_TRUE(OpenPlaygroundHere(callback));
369 }
370 
371 TEST_P(ComputeSubgroupTest, QuadAndCubicInOnePath) {
372  using SS = StrokeComputeShader;
373 
374  auto context = GetContext();
375  ASSERT_TRUE(context);
376  ASSERT_TRUE(context->GetCapabilities()->SupportsComputeSubgroups());
377 
378  auto vertex_buffer = CreateHostVisibleDeviceBuffer<SS::VertexBuffer<2048>>(
379  context, "VertexBuffer");
380  auto vertex_buffer_count =
381  CreateHostVisibleDeviceBuffer<SS::VertexBufferCount>(context,
382  "VertexBufferCount");
383 
384  auto path = PathBuilder{}
385  .AddCubicCurve({140, 20}, {73, 20}, {20, 74}, {20, 140})
386  .AddQuadraticCurve({20, 140}, {93, 90}, {100, 42})
387  .TakePath();
388 
389  auto tessellator = ComputeTessellator{};
390 
391  fml::AutoResetWaitableEvent latch;
392 
393  auto host_buffer = HostBuffer::Create(context->GetResourceAllocator());
394  auto status = tessellator.Tessellate(
395  path, *host_buffer, context, DeviceBuffer::AsBufferView(vertex_buffer),
396  DeviceBuffer::AsBufferView(vertex_buffer_count),
397  [&latch](CommandBuffer::Status status) {
398  EXPECT_EQ(status, CommandBuffer::Status::kCompleted);
399  latch.Signal();
400  });
401 
402  ASSERT_EQ(status, ComputeTessellator::Status::kOk);
403 
404  auto callback = [&](RenderPass& pass) -> bool {
405  ContentContext renderer(context, nullptr);
406  if (!renderer.IsValid()) {
407  return false;
408  }
409 
411 
412  pass.SetCommandLabel("Draw Stroke");
413  pass.SetStencilReference(0);
414 
415  ContentContextOptions options;
416  options.sample_count = pass.GetRenderTarget().GetSampleCount();
418  pass.GetRenderTarget().GetRenderTargetPixelFormat();
420  pass.GetRenderTarget().GetStencilAttachment().has_value();
423 
424  options.stencil_mode =
426 
427  pass.SetPipeline(renderer.GetSolidFillPipeline(options));
428 
429  auto count = reinterpret_cast<SS::VertexBufferCount*>(
430  vertex_buffer_count->OnGetContents())
431  ->count;
432 
433  pass.SetVertexBuffer(
435  .vertex_count = count,
436  .index_type = IndexType::kNone});
437 
438  VS::FrameInfo frame_info;
439  auto world_matrix = Matrix::MakeScale(GetContentScale());
440  frame_info.mvp = pass.GetOrthographicTransform() * world_matrix;
441  frame_info.color = Color::Red().Premultiply();
442  VS::BindFrameInfo(
443  pass, renderer.GetTransientsBuffer().EmplaceUniform(frame_info));
444 
445  return pass.Draw().ok();
446  };
447  ASSERT_TRUE(OpenPlaygroundHere(callback));
448 
449  // The latch is down here because it's expected that on Metal the backend
450  // will take care of synchronizing the buffer between the compute and render
451  // pass usages, since it's not MTLHeap allocated. However, if playgrounds
452  // are disabled, no render pass actually gets submitted and we need to do a
453  // CPU latch here.
454  latch.Wait();
455 
456  auto vertex_count = reinterpret_cast<SS::VertexBufferCount*>(
457  vertex_buffer_count->OnGetContents())
458  ->count;
459  EXPECT_EQ(vertex_count, golden_cubic_and_quad_points.size());
460  auto vertex_buffer_data =
461  reinterpret_cast<SS::VertexBuffer<2048>*>(vertex_buffer->OnGetContents());
462  for (size_t i = 0; i < vertex_count; i++) {
463  EXPECT_LT(std::abs(golden_cubic_and_quad_points[i].x -
464  vertex_buffer_data->position[i].x),
465  1e-3);
466  EXPECT_LT(std::abs(golden_cubic_and_quad_points[i].y -
467  vertex_buffer_data->position[i].y),
468  1e-3);
469  }
470 }
471 
472 } // namespace testing
473 } // namespace impeller
impeller::DeviceBuffer::AsBufferView
static BufferView AsBufferView(std::shared_ptr< DeviceBuffer > buffer)
Create a buffer view of this entire buffer.
Definition: device_buffer.cc:18
golden_paths.h
impeller::ComputeTessellator::Status::kCommandInvalid
@ kCommandInvalid
path.h
host_buffer.h
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::ComputeTessellator::Status::kTooManyComponents
@ kTooManyComponents
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:264
impeller::VertexBuffer
Definition: vertex_buffer.h:13
formats.h
impeller::PathBuilder
Definition: path_builder.h:14
impeller::HostBuffer::Create
static std::shared_ptr< HostBuffer > Create(const std::shared_ptr< Allocator > &allocator)
Definition: host_buffer.cc:20
impeller::ContentContextOptions::has_depth_stencil_attachments
bool has_depth_stencil_attachments
Definition: content_context.h:339
impeller::ContentContextOptions::blend_mode
BlendMode blend_mode
Definition: content_context.h:333
impeller::ComputePlaygroundTest
Definition: compute_playground_test.h:19
impeller::testing::golden_cubic_and_quad_points
std::vector< Point > golden_cubic_and_quad_points
Definition: golden_paths.h:16
impeller::VertexBuffer::vertex_buffer
BufferView vertex_buffer
Definition: vertex_buffer.h:14
compute_tessellator.h
stroke_width
const Scalar stroke_width
Definition: stroke_path_geometry.cc:293
impeller::ComputeTessellator
A utility that generates triangles of the specified fill type given a path.
Definition: compute_tessellator.h:20
path_builder.h
impeller::VS
SolidFillVertexShader VS
Definition: stroke_path_geometry.cc:15
impeller::ComputeTessellator::Status::kOk
@ kOk
impeller::PrimitiveType::kTriangleStrip
@ kTriangleStrip
impeller::ContentContextOptions::StencilMode::kLegacyClipIncrement
@ kLegacyClipIncrement
Increment the stencil heightmap.
impeller::ContentContextOptions::color_attachment_pixel_format
PixelFormat color_attachment_pixel_format
Definition: content_context.h:338
render_pass.h
skia_conversions.h
impeller::ComputeTessellator::SetStrokeWidth
ComputeTessellator & SetStrokeWidth(Scalar value)
Definition: compute_tessellator.cc:38
impeller::ContentContext::GetSolidFillPipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > GetSolidFillPipeline(ContentContextOptions opts) const
Definition: content_context.h:475
impeller::PathBuilder::AddCubicCurve
PathBuilder & AddCubicCurve(Point p1, Point cp1, Point cp2, Point p2)
Move to point p1, then insert a cubic curve from p1 to p2 with control points cp1 and cp2.
Definition: path_builder.cc:103
impeller::IndexType::kNone
@ kNone
Does not use the index buffer.
compute_pipeline_builder.h
impeller::testing::INSTANTIATE_COMPUTE_SUITE
INSTANTIATE_COMPUTE_SUITE(ComputeSubgroupTest)
impeller::ContentContextOptions::stencil_mode
StencilMode stencil_mode
Definition: content_context.h:335
strings.h
impeller::ContentContextOptions::primitive_type
PrimitiveType primitive_type
Definition: content_context.h:337
pipeline_library.h
impeller::Close
void Close(PathBuilder *builder)
Definition: tessellator.cc:36
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:33
command_buffer.h
content_context.h
impeller::LineTo
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
Definition: tessellator.cc:22
impeller::testing::TEST_P
TEST_P(AiksTest, CanRenderMaskBlurHugeSigma)
Definition: aiks_blur_unittests.cc:23
impeller::CommandBuffer::Status::kCompleted
@ kCompleted
impeller::ContentContextOptions::sample_count
SampleCount sample_count
Definition: content_context.h:332
impeller::ComputeTessellator::Tessellate
Status Tessellate(const Path &path, HostBuffer &host_buffer, const std::shared_ptr< Context > &context, BufferView vertex_buffer, BufferView vertex_buffer_count, const CommandBuffer::CompletionCallback &callback=nullptr) const
Generates triangles from the path. If the data needs to be synchronized back to the CPU,...
Definition: compute_tessellator.cc:64
impeller::PathBuilder::MoveTo
PathBuilder & MoveTo(Point point, bool relative=false)
Definition: path_builder.cc:33
impeller::HostBuffer::EmplaceUniform
BufferView EmplaceUniform(const UniformType &uniform)
Emplace uniform data onto the host buffer. Ensure that backend specific uniform alignment requirement...
Definition: host_buffer.h:50
impeller::BlendMode::kSourceIn
@ kSourceIn
compute_playground_test.h
impeller::CommandBuffer::Status
Status
Definition: command_buffer.h:49
path_component.h
impeller::RenderPipelineT::VertexShader
VertexShader_ VertexShader
Definition: pipeline.h:93
impeller::ContentContextOptions
Definition: content_context.h:288
impeller
Definition: aiks_blur_unittests.cc:20
impeller::ContentContext::IsValid
bool IsValid() const
Definition: content_context.cc:474
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::ContentContext
Definition: content_context.h:392
impeller::skia_conversions::ToPath
Path ToPath(const SkPath &path, Point shift)
Definition: skia_conversions.cc:49
impeller::Color::Premultiply
constexpr Color Premultiply() const
Definition: color.h:214
impeller::ContentContext::GetTransientsBuffer
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
Definition: content_context.h:833