Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
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"
19#include "impeller/fixtures/sample.comp.h"
20#include "impeller/fixtures/stage1.comp.h"
21#include "impeller/fixtures/stage2.comp.h"
29#include "impeller/renderer/path_polyline.comp.h"
32#include "impeller/renderer/stroke.comp.h"
33#include "third_party/imgui/imgui.h"
35
36namespace impeller {
37namespace testing {
38
41
42TEST_P(ComputeSubgroupTest, CapabilitiesSuportSubgroups) {
43 auto context = GetContext();
44 ASSERT_TRUE(context);
45 ASSERT_TRUE(context->GetCapabilities()->SupportsComputeSubgroups());
46}
47
48TEST_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
139 options.sample_count = pass.GetRenderTarget().GetSampleCount();
140 options.color_attachment_pixel_format =
141 pass.GetRenderTarget().GetRenderTargetPixelFormat();
142 options.has_depth_stencil_attachments =
143 pass.GetRenderTarget().GetStencilAttachment().has_value();
144 options.blend_mode = BlendMode::kSourceIn;
145 options.primitive_type = PrimitiveType::kTriangleStrip;
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 =
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
337 options.sample_count = pass.GetRenderTarget().GetSampleCount();
338 options.color_attachment_pixel_format =
339 pass.GetRenderTarget().GetRenderTargetPixelFormat();
340 options.has_depth_stencil_attachments =
341 pass.GetRenderTarget().GetStencilAttachment().has_value();
342 options.blend_mode = BlendMode::kSourceIn;
343 options.primitive_type = PrimitiveType::kTriangleStrip;
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
371TEST_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
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
416 options.sample_count = pass.GetRenderTarget().GetSampleCount();
417 options.color_attachment_pixel_format =
418 pass.GetRenderTarget().GetRenderTargetPixelFormat();
419 options.has_depth_stencil_attachments =
420 pass.GetRenderTarget().GetStencilAttachment().has_value();
421 options.blend_mode = BlendMode::kSourceIn;
422 options.primitive_type = PrimitiveType::kTriangleStrip;
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
const char * options
int count
static bool FromSVGString(const char str[], SkPath *)
A utility that generates triangles of the specified fill type given a path.
ComputeTessellator & SetStrokeWidth(Scalar value)
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,...
static BufferView AsBufferView(std::shared_ptr< DeviceBuffer > buffer)
Create a buffer view of this entire buffer.
static std::shared_ptr< HostBuffer > Create(const std::shared_ptr< Allocator > &allocator)
Path TakePath(FillType fill=FillType::kNonZero)
PathBuilder & MoveTo(Point point, bool relative=false)
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.
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition render_pass.h:33
#define INSTANTIATE_COMPUTE_SUITE(playground)
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
double y
double x
Path ToPath(const SkPath &path, Point shift)
std::vector< Point > golden_cubic_and_quad_points
TEST_P(AiksTest, CanRenderAdvancedBlendColorFilterWithSaveLayer)
@ kNone
Does not use the index buffer.
float Scalar
Definition scalar.h:18
SolidFillVertexShader VS
void LineTo(PathBuilder *builder, Scalar x, Scalar y)
void Close(PathBuilder *builder)
const Scalar stroke_width
static constexpr Color Red()
Definition color.h:264
constexpr Color Premultiply() const
Definition color.h:214
static constexpr Matrix MakeScale(const Vector3 &s)
Definition matrix.h:104