| Flutter Impeller |

renderer_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 "flutter/fml/logging.h"
6 #include "flutter/testing/testing.h"
7 #include "impeller/base/strings.h"
8 #include "impeller/core/device_buffer_descriptor.h"
9 #include "impeller/core/formats.h"
10 #include "impeller/core/sampler.h"
11 #include "impeller/core/sampler_descriptor.h"
12 #include "impeller/fixtures/array.frag.h"
13 #include "impeller/fixtures/array.vert.h"
14 #include "impeller/fixtures/box_fade.frag.h"
15 #include "impeller/fixtures/box_fade.vert.h"
16 #include "impeller/fixtures/colors.frag.h"
17 #include "impeller/fixtures/colors.vert.h"
18 #include "impeller/fixtures/impeller.frag.h"
19 #include "impeller/fixtures/impeller.vert.h"
20 #include "impeller/fixtures/inactive_uniforms.frag.h"
21 #include "impeller/fixtures/inactive_uniforms.vert.h"
22 #include "impeller/fixtures/instanced_draw.frag.h"
23 #include "impeller/fixtures/instanced_draw.vert.h"
24 #include "impeller/fixtures/mipmaps.frag.h"
25 #include "impeller/fixtures/mipmaps.vert.h"
26 #include "impeller/fixtures/test_texture.frag.h"
27 #include "impeller/fixtures/test_texture.vert.h"
28 #include "impeller/geometry/path_builder.h"
29 #include "impeller/image/compressed_image.h"
30 #include "impeller/image/decompressed_image.h"
31 #include "impeller/playground/playground_test.h"
32 #include "impeller/renderer/command.h"
33 #include "impeller/renderer/command_buffer.h"
34 #include "impeller/renderer/pipeline_builder.h"
35 #include "impeller/renderer/pipeline_library.h"
36 #include "impeller/renderer/renderer.h"
37 #include "impeller/renderer/sampler_library.h"
38 #include "impeller/renderer/surface.h"
39 #include "impeller/renderer/vertex_buffer_builder.h"
40 #include "impeller/tessellator/tessellator.h"
41 #include "third_party/imgui/imgui.h"
42
43 // TODO(zanderso): https://github.com/flutter/flutter/issues/127701
44 // NOLINTBEGIN(bugprone-unchecked-optional-access)
45
46 namespace impeller {
47 namespace testing {
48
49 using RendererTest = PlaygroundTest;
50 INSTANTIATE_PLAYGROUND_SUITE(RendererTest);
51
52 TEST_P(RendererTest, CanCreateBoxPrimitive) {
53 using VS = BoxFadeVertexShader;
54 using FS = BoxFadeFragmentShader;
55 auto context = GetContext();
56 ASSERT_TRUE(context);
57 using BoxPipelineBuilder = PipelineBuilder<VS, FS>;
58 auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context);
59 ASSERT_TRUE(desc.has_value());
60 desc->SetSampleCount(SampleCount::kCount4);
61 desc->SetStencilAttachmentDescriptors(std::nullopt);
62
63 // Vertex buffer.
64 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
65 vertex_builder.SetLabel("Box");
66 vertex_builder.AddVertices({
67 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
68 {{800, 100, 0.0}, {1.0, 0.0}}, // 2
69 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
70 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
71 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
72 {{100, 800, 0.0}, {0.0, 1.0}}, // 4
73 });
74 auto vertex_buffer =
75 vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
76 ASSERT_TRUE(vertex_buffer);
77
78 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
79 auto boston = CreateTextureForFixture("boston.jpg");
80 ASSERT_TRUE(bridge && boston);
81 auto sampler = context->GetSamplerLibrary()->GetSampler({});
82 ASSERT_TRUE(sampler);
83 SinglePassCallback callback = [&](RenderPass& pass) {
84 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
85 static bool wireframe;
86 ImGui::Checkbox("Wireframe", &wireframe);
87 ImGui::End();
88
89 desc->SetPolygonMode(wireframe ? PolygonMode::kLine : PolygonMode::kFill);
90 auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get();
91
92 assert(pipeline && pipeline->IsValid());
93
94 Command cmd;
95 DEBUG_COMMAND_INFO(cmd, "Box");
96 cmd.pipeline = pipeline;
97
98 cmd.BindVertices(vertex_buffer);
99
100 VS::UniformBuffer uniforms;
101 uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
102 Matrix::MakeScale(GetContentScale());
103 VS::BindUniformBuffer(cmd,
104 pass.GetTransientsBuffer().EmplaceUniform(uniforms));
105
106 FS::FrameInfo frame_info;
107 frame_info.current_time = GetSecondsElapsed();
108 frame_info.cursor_position = GetCursorPosition();
109 frame_info.window_size.x = GetWindowSize().width;
110 frame_info.window_size.y = GetWindowSize().height;
111
112 FS::BindFrameInfo(cmd,
113 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
114 FS::BindContents1(cmd, boston, sampler);
115 FS::BindContents2(cmd, bridge, sampler);
116 if (!pass.AddCommand(std::move(cmd))) {
117 return false;
118 }
119 return true;
120 };
121 OpenPlaygroundHere(callback);
122 }
123
124 TEST_P(RendererTest, CanRenderPerspectiveCube) {
125 using VS = ColorsVertexShader;
126 using FS = ColorsFragmentShader;
127 auto context = GetContext();
128 ASSERT_TRUE(context);
129 auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
130 ASSERT_TRUE(desc.has_value());
131 desc->SetCullMode(CullMode::kBackFace);
132 desc->SetWindingOrder(WindingOrder::kCounterClockwise);
133 desc->SetSampleCount(SampleCount::kCount4);
134 desc->SetStencilAttachmentDescriptors(std::nullopt);
135 auto pipeline =
136 context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
137 ASSERT_TRUE(pipeline);
138
139 struct Cube {
140 VS::PerVertexData vertices[8] = {
141 // -Z
142 {{-1, -1, -1}, Color::Red()},
143 {{1, -1, -1}, Color::Yellow()},
144 {{1, 1, -1}, Color::Green()},
145 {{-1, 1, -1}, Color::Blue()},
146 // +Z
147 {{-1, -1, 1}, Color::Green()},
148 {{1, -1, 1}, Color::Blue()},
149 {{1, 1, 1}, Color::Red()},
150 {{-1, 1, 1}, Color::Yellow()},
151 };
152 uint16_t indices[36] = {
153 1, 5, 2, 2, 5, 6, // +X
154 4, 0, 7, 7, 0, 3, // -X
155 4, 5, 0, 0, 5, 1, // +Y
156 3, 2, 7, 7, 2, 6, // -Y
157 5, 4, 6, 6, 4, 7, // +Z
158 0, 1, 3, 3, 1, 2, // -Z
159 };
160 } cube;
161
162 VertexBuffer vertex_buffer;
163 {
164 auto device_buffer = context->GetResourceAllocator()->CreateBufferWithCopy(
165 reinterpret_cast<uint8_t*>(&cube), sizeof(cube));
166 vertex_buffer.vertex_buffer = {
167 .buffer = device_buffer,
168 .range = Range(offsetof(Cube, vertices), sizeof(Cube::vertices))};
169 vertex_buffer.index_buffer = {
170 .buffer = device_buffer,
171 .range = Range(offsetof(Cube, indices), sizeof(Cube::indices))};
172 vertex_buffer.vertex_count = 36;
173 vertex_buffer.index_type = IndexType::k16bit;
174 }
175
176 auto sampler = context->GetSamplerLibrary()->GetSampler({});
177 ASSERT_TRUE(sampler);
178
179 Vector3 euler_angles;
180 SinglePassCallback callback = [&](RenderPass& pass) {
181 static Degrees fov_y(60);
182 static Scalar distance = 10;
183
184 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
185 ImGui::SliderFloat("Field of view", &fov_y.degrees, 0, 180);
186 ImGui::SliderFloat("Camera distance", &distance, 0, 30);
187 ImGui::End();
188
189 Command cmd;
190 DEBUG_COMMAND_INFO(cmd, "Perspective Cube");
191 cmd.pipeline = pipeline;
192
193 cmd.BindVertices(vertex_buffer);
194
195 VS::UniformBuffer uniforms;
196 Scalar time = GetSecondsElapsed();
197 euler_angles = Vector3(0.19 * time, 0.7 * time, 0.43 * time);
198
199 uniforms.mvp =
200 Matrix::MakePerspective(fov_y, pass.GetRenderTargetSize(), 0, 10) *
201 Matrix::MakeTranslation({0, 0, distance}) *
202 Matrix::MakeRotationX(Radians(euler_angles.x)) *
203 Matrix::MakeRotationY(Radians(euler_angles.y)) *
204 Matrix::MakeRotationZ(Radians(euler_angles.z));
205 VS::BindUniformBuffer(cmd,
206 pass.GetTransientsBuffer().EmplaceUniform(uniforms));
207 if (!pass.AddCommand(std::move(cmd))) {
208 return false;
209 }
210 return true;
211 };
212 OpenPlaygroundHere(callback);
213 }
214
215 TEST_P(RendererTest, CanRenderMultiplePrimitives) {
216 using VS = BoxFadeVertexShader;
217 using FS = BoxFadeFragmentShader;
218 auto context = GetContext();
219 ASSERT_TRUE(context);
220 using BoxPipelineBuilder = PipelineBuilder<VS, FS>;
221 auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context);
222 ASSERT_TRUE(desc.has_value());
223 desc->SetSampleCount(SampleCount::kCount4);
224 desc->SetStencilAttachmentDescriptors(std::nullopt);
225 auto box_pipeline =
226 context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
227 ASSERT_TRUE(box_pipeline);
228
229 // Vertex buffer.
230 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
231 vertex_builder.SetLabel("Box");
232 vertex_builder.AddVertices({
233 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
234 {{800, 100, 0.0}, {1.0, 0.0}}, // 2
235 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
236 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
237 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
238 {{100, 800, 0.0}, {0.0, 1.0}}, // 4
239 });
240 auto vertex_buffer =
241 vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
242 ASSERT_TRUE(vertex_buffer);
243
244 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
245 auto boston = CreateTextureForFixture("boston.jpg");
246 ASSERT_TRUE(bridge && boston);
247 auto sampler = context->GetSamplerLibrary()->GetSampler({});
248 ASSERT_TRUE(sampler);
249
250 SinglePassCallback callback = [&](RenderPass& pass) {
251 Command cmd;
252 DEBUG_COMMAND_INFO(cmd, "Box");
253 cmd.pipeline = box_pipeline;
254
255 cmd.BindVertices(vertex_buffer);
256
257 FS::FrameInfo frame_info;
258 frame_info.current_time = GetSecondsElapsed();
259 frame_info.cursor_position = GetCursorPosition();
260 frame_info.window_size.x = GetWindowSize().width;
261 frame_info.window_size.y = GetWindowSize().height;
262
263 FS::BindFrameInfo(cmd,
264 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
265 FS::BindContents1(cmd, boston, sampler);
266 FS::BindContents2(cmd, bridge, sampler);
267
268 for (size_t i = 0; i < 1; i++) {
269 for (size_t j = 0; j < 1; j++) {
270 VS::UniformBuffer uniforms;
271 uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
272 Matrix::MakeScale(GetContentScale()) *
273 Matrix::MakeTranslation({i * 50.0f, j * 50.0f, 0.0f});
274 VS::BindUniformBuffer(
275 cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms));
276 if (!pass.AddCommand(std::move(cmd))) {
277 return false;
278 }
279 }
280 }
281
282 return true;
283 };
284 OpenPlaygroundHere(callback);
285 }
286
287 TEST_P(RendererTest, CanRenderToTexture) {
288 using VS = BoxFadeVertexShader;
289 using FS = BoxFadeFragmentShader;
290 auto context = GetContext();
291 ASSERT_TRUE(context);
292 using BoxPipelineBuilder = PipelineBuilder<VS, FS>;
293 auto pipeline_desc =
294 BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context);
295 pipeline_desc->SetSampleCount(SampleCount::kCount1);
296
297 ASSERT_TRUE(pipeline_desc.has_value());
298 auto box_pipeline =
299 context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
300 ASSERT_TRUE(box_pipeline);
301
302 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
303 vertex_builder.SetLabel("Box");
304 vertex_builder.AddVertices({
305 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
306 {{800, 100, 0.0}, {1.0, 0.0}}, // 2
307 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
308 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
309 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
310 {{100, 800, 0.0}, {0.0, 1.0}}, // 4
311 });
312 auto vertex_buffer =
313 vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
314 ASSERT_TRUE(vertex_buffer);
315
316 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
317 auto boston = CreateTextureForFixture("boston.jpg");
318 ASSERT_TRUE(bridge && boston);
319 auto sampler = context->GetSamplerLibrary()->GetSampler({});
320 ASSERT_TRUE(sampler);
321 std::shared_ptr<RenderPass> r2t_pass;
322 auto cmd_buffer = context->CreateCommandBuffer();
323 ASSERT_TRUE(cmd_buffer);
324 {
325 ColorAttachment color0;
326 color0.load_action = LoadAction::kClear;
327 color0.store_action = StoreAction::kStore;
328
329 TextureDescriptor texture_descriptor;
330 ASSERT_NE(pipeline_desc->GetColorAttachmentDescriptor(0u), nullptr);
331 texture_descriptor.format =
332 pipeline_desc->GetColorAttachmentDescriptor(0u)->format;
333 texture_descriptor.storage_mode = StorageMode::kHostVisible;
334 texture_descriptor.size = {400, 400};
335 texture_descriptor.mip_count = 1u;
336 texture_descriptor.usage =
337 static_cast<TextureUsageMask>(TextureUsage::kRenderTarget);
338
339 color0.texture =
340 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
341
342 ASSERT_TRUE(color0.IsValid());
343
344 color0.texture->SetLabel("r2t_target");
345
346 StencilAttachment stencil0;
347 stencil0.load_action = LoadAction::kClear;
348 stencil0.store_action = StoreAction::kDontCare;
349 TextureDescriptor stencil_texture_desc;
350 stencil_texture_desc.storage_mode = StorageMode::kDeviceTransient;
351 stencil_texture_desc.size = texture_descriptor.size;
352 stencil_texture_desc.format = PixelFormat::kS8UInt;
353 stencil_texture_desc.usage =
354 static_cast<TextureUsageMask>(TextureUsage::kRenderTarget);
355 stencil0.texture =
356 context->GetResourceAllocator()->CreateTexture(stencil_texture_desc);
357
358 RenderTarget r2t_desc;
359 r2t_desc.SetColorAttachment(color0, 0u);
360 r2t_desc.SetStencilAttachment(stencil0);
361 r2t_pass = cmd_buffer->CreateRenderPass(r2t_desc);
362 ASSERT_TRUE(r2t_pass && r2t_pass->IsValid());
363 }
364
365 Command cmd;
366 DEBUG_COMMAND_INFO(cmd, "Box");
367 cmd.pipeline = box_pipeline;
368
369 cmd.BindVertices(vertex_buffer);
370
371 FS::FrameInfo frame_info;
372 frame_info.current_time = GetSecondsElapsed();
373 frame_info.cursor_position = GetCursorPosition();
374 frame_info.window_size.x = GetWindowSize().width;
375 frame_info.window_size.y = GetWindowSize().height;
376
377 FS::BindFrameInfo(cmd,
378 r2t_pass->GetTransientsBuffer().EmplaceUniform(frame_info));
379 FS::BindContents1(cmd, boston, sampler);
380 FS::BindContents2(cmd, bridge, sampler);
381
382 VS::UniformBuffer uniforms;
383 uniforms.mvp = Matrix::MakeOrthographic(ISize{1024, 768}) *
384 Matrix::MakeTranslation({50.0f, 50.0f, 0.0f});
385 VS::BindUniformBuffer(
386 cmd, r2t_pass->GetTransientsBuffer().EmplaceUniform(uniforms));
387 ASSERT_TRUE(r2t_pass->AddCommand(std::move(cmd)));
388 ASSERT_TRUE(r2t_pass->EncodeCommands());
389 }
390
391 TEST_P(RendererTest, CanRenderInstanced) {
392 if (GetParam() == PlaygroundBackend::kOpenGLES) {
393 GTEST_SKIP_("Instancing is not supported on OpenGL.");
394 }
395 using VS = InstancedDrawVertexShader;
396 using FS = InstancedDrawFragmentShader;
397
398 VertexBufferBuilder<VS::PerVertexData> builder;
399
400 ASSERT_EQ(Tessellator::Result::kSuccess,
401 Tessellator{}.Tessellate(
402 FillType::kPositive,
403 PathBuilder{}
404 .AddRect(Rect::MakeXYWH(10, 10, 100, 100))
405 .TakePath()
406 .CreatePolyline(1.0f),
407 [&builder](const float* vertices, size_t vertices_count,
408 const uint16_t* indices, size_t indices_count) {
409 for (auto i = 0u; i < vertices_count * 2; i += 2) {
410 VS::PerVertexData data;
411 data.vtx = {vertices[i], vertices[i + 1]};
412 builder.AppendVertex(data);
413 }
414 for (auto i = 0u; i < indices_count; i++) {
415 builder.AppendIndex(indices[i]);
416 }
417 return true;
418 }));
419
420 ASSERT_NE(GetContext(), nullptr);
421 auto pipeline =
422 GetContext()
423 ->GetPipelineLibrary()
424 ->GetPipeline(PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(
425 *GetContext())
426 ->SetSampleCount(SampleCount::kCount4)
427 .SetStencilAttachmentDescriptors(std::nullopt))
428
429 .Get();
430 ASSERT_TRUE(pipeline && pipeline->IsValid());
431
432 Command cmd;
433 cmd.pipeline = pipeline;
434 DEBUG_COMMAND_INFO(cmd, "InstancedDraw");
435
436 static constexpr size_t kInstancesCount = 5u;
437 VS::InstanceInfo<kInstancesCount> instances;
438 for (size_t i = 0; i < kInstancesCount; i++) {
439 instances.colors[i] = Color::Random();
440 }
441
442 ASSERT_TRUE(OpenPlaygroundHere([&](RenderPass& pass) -> bool {
443 VS::FrameInfo frame_info;
444 frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
445 Matrix::MakeScale(GetContentScale());
446 VS::BindFrameInfo(cmd,
447 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
448 VS::BindInstanceInfo(
449 cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(instances));
450 cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
451
452 cmd.instance_count = kInstancesCount;
453 pass.AddCommand(std::move(cmd));
454 return true;
455 }));
456 }
457
458 TEST_P(RendererTest, CanBlitTextureToTexture) {
459 auto context = GetContext();
460 ASSERT_TRUE(context);
461
462 using VS = MipmapsVertexShader;
463 using FS = MipmapsFragmentShader;
464 auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
465 ASSERT_TRUE(desc.has_value());
466 desc->SetSampleCount(SampleCount::kCount4);
467 desc->SetStencilAttachmentDescriptors(std::nullopt);
468 auto mipmaps_pipeline =
469 context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
470 ASSERT_TRUE(mipmaps_pipeline);
471
472 TextureDescriptor texture_desc;
473 texture_desc.storage_mode = StorageMode::kHostVisible;
474 texture_desc.format = PixelFormat::kR8G8B8A8UNormInt;
475 texture_desc.size = {800, 600};
476 texture_desc.mip_count = 1u;
477 texture_desc.usage =
478 static_cast<TextureUsageMask>(TextureUsage::kRenderTarget) |
479 static_cast<TextureUsageMask>(TextureUsage::kShaderRead);
480 auto texture = context->GetResourceAllocator()->CreateTexture(texture_desc);
481 ASSERT_TRUE(texture);
482
483 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
484 auto boston = CreateTextureForFixture("boston.jpg");
485 ASSERT_TRUE(bridge && boston);
486 auto sampler = context->GetSamplerLibrary()->GetSampler({});
487 ASSERT_TRUE(sampler);
488
489 // Vertex buffer.
490 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
491 vertex_builder.SetLabel("Box");
492 auto size = Point(boston->GetSize());
493 vertex_builder.AddVertices({
494 {{0, 0}, {0.0, 0.0}}, // 1
495 {{size.x, 0}, {1.0, 0.0}}, // 2
496 {{size.x, size.y}, {1.0, 1.0}}, // 3
497 {{0, 0}, {0.0, 0.0}}, // 1
498 {{size.x, size.y}, {1.0, 1.0}}, // 3
499 {{0, size.y}, {0.0, 1.0}}, // 4
500 });
501 auto vertex_buffer =
502 vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
503 ASSERT_TRUE(vertex_buffer);
504
505 Renderer::RenderCallback callback = [&](RenderTarget& render_target) {
506 auto buffer = context->CreateCommandBuffer();
507 if (!buffer) {
508 return false;
509 }
510 buffer->SetLabel("Playground Command Buffer");
511
512 {
513 auto pass = buffer->CreateBlitPass();
514 if (!pass) {
515 return false;
516 }
517 pass->SetLabel("Playground Blit Pass");
518
519 if (render_target.GetColorAttachments().empty()) {
520 return false;
521 }
522
523 // Blit `bridge` to the top left corner of the texture.
524 pass->AddCopy(bridge, texture);
525
526 if (!pass->EncodeCommands(context->GetResourceAllocator())) {
527 return false;
528 }
529 }
530
531 {
532 auto pass = buffer->CreateRenderPass(render_target);
533 if (!pass) {
534 return false;
535 }
536 pass->SetLabel("Playground Render Pass");
537 {
538 Command cmd;
539 DEBUG_COMMAND_INFO(cmd, "Image");
540 cmd.pipeline = mipmaps_pipeline;
541
542 cmd.BindVertices(vertex_buffer);
543
544 VS::FrameInfo frame_info;
545 frame_info.mvp = Matrix::MakeOrthographic(pass->GetRenderTargetSize()) *
546 Matrix::MakeScale(GetContentScale());
547 VS::BindFrameInfo(
548 cmd, pass->GetTransientsBuffer().EmplaceUniform(frame_info));
549
550 FS::FragInfo frag_info;
551 frag_info.lod = 0;
552 FS::BindFragInfo(cmd,
553 pass->GetTransientsBuffer().EmplaceUniform(frag_info));
554
555 auto sampler = context->GetSamplerLibrary()->GetSampler({});
556 FS::BindTex(cmd, texture, sampler);
557
558 pass->AddCommand(std::move(cmd));
559 }
560 pass->EncodeCommands();
561 }
562
563 if (!buffer->SubmitCommands()) {
564 return false;
565 }
566 return true;
567 };
568 OpenPlaygroundHere(callback);
569 }
570
571 TEST_P(RendererTest, CanBlitTextureToBuffer) {
572 auto context = GetContext();
573 ASSERT_TRUE(context);
574
575 using VS = MipmapsVertexShader;
576 using FS = MipmapsFragmentShader;
577 auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
578 ASSERT_TRUE(desc.has_value());
579 desc->SetSampleCount(SampleCount::kCount4);
580 desc->SetStencilAttachmentDescriptors(std::nullopt);
581 auto mipmaps_pipeline =
582 context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
583 ASSERT_TRUE(mipmaps_pipeline);
584
585 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
586 auto boston = CreateTextureForFixture("boston.jpg");
587 ASSERT_TRUE(bridge && boston);
588 auto sampler = context->GetSamplerLibrary()->GetSampler({});
589 ASSERT_TRUE(sampler);
590
591 TextureDescriptor texture_desc;
592 texture_desc.storage_mode = StorageMode::kHostVisible;
593 texture_desc.format = PixelFormat::kR8G8B8A8UNormInt;
594 texture_desc.size = bridge->GetTextureDescriptor().size;
595 texture_desc.mip_count = 1u;
596 texture_desc.usage =
597 static_cast<TextureUsageMask>(TextureUsage::kRenderTarget) |
598 static_cast<TextureUsageMask>(TextureUsage::kShaderWrite) |
599 static_cast<TextureUsageMask>(TextureUsage::kShaderRead);
600 DeviceBufferDescriptor device_buffer_desc;
601 device_buffer_desc.storage_mode = StorageMode::kHostVisible;
602 device_buffer_desc.size =
603 bridge->GetTextureDescriptor().GetByteSizeOfBaseMipLevel();
604 auto device_buffer =
605 context->GetResourceAllocator()->CreateBuffer(device_buffer_desc);
606
607 // Vertex buffer.
608 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
609 vertex_builder.SetLabel("Box");
610 auto size = Point(boston->GetSize());
611 vertex_builder.AddVertices({
612 {{0, 0}, {0.0, 0.0}}, // 1
613 {{size.x, 0}, {1.0, 0.0}}, // 2
614 {{size.x, size.y}, {1.0, 1.0}}, // 3
615 {{0, 0}, {0.0, 0.0}}, // 1
616 {{size.x, size.y}, {1.0, 1.0}}, // 3
617 {{0, size.y}, {0.0, 1.0}}, // 4
618 });
619 auto vertex_buffer =
620 vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
621 ASSERT_TRUE(vertex_buffer);
622
623 Renderer::RenderCallback callback = [&](RenderTarget& render_target) {
624 {
625 auto buffer = context->CreateCommandBuffer();
626 if (!buffer) {
627 return false;
628 }
629 buffer->SetLabel("Playground Command Buffer");
630 auto pass = buffer->CreateBlitPass();
631 if (!pass) {
632 return false;
633 }
634 pass->SetLabel("Playground Blit Pass");
635
636 if (render_target.GetColorAttachments().empty()) {
637 return false;
638 }
639
640 // Blit `bridge` to the top left corner of the texture.
641 pass->AddCopy(bridge, device_buffer);
642
643 pass->EncodeCommands(context->GetResourceAllocator());
644
645 if (!buffer->SubmitCommands()) {
646 return false;
647 }
648 }
649
650 {
651 auto buffer = context->CreateCommandBuffer();
652 if (!buffer) {
653 return false;
654 }
655 buffer->SetLabel("Playground Command Buffer");
656
657 auto pass = buffer->CreateRenderPass(render_target);
658 if (!pass) {
659 return false;
660 }
661 pass->SetLabel("Playground Render Pass");
662 {
663 Command cmd;
664 DEBUG_COMMAND_INFO(cmd, "Image");
665 cmd.pipeline = mipmaps_pipeline;
666
667 cmd.BindVertices(vertex_buffer);
668
669 VS::FrameInfo frame_info;
670 frame_info.mvp = Matrix::MakeOrthographic(pass->GetRenderTargetSize()) *
671 Matrix::MakeScale(GetContentScale());
672 VS::BindFrameInfo(
673 cmd, pass->GetTransientsBuffer().EmplaceUniform(frame_info));
674
675 FS::FragInfo frag_info;
676 frag_info.lod = 0;
677 FS::BindFragInfo(cmd,
678 pass->GetTransientsBuffer().EmplaceUniform(frag_info));
679
680 auto sampler = context->GetSamplerLibrary()->GetSampler({});
681 auto buffer_view = device_buffer->AsBufferView();
682 auto texture =
683 context->GetResourceAllocator()->CreateTexture(texture_desc);
684 if (!texture->SetContents(buffer_view.contents,
685 buffer_view.range.length)) {
686 VALIDATION_LOG << "Could not upload texture to device memory";
687 return false;
688 }
689 FS::BindTex(cmd, texture, sampler);
690
691 pass->AddCommand(std::move(cmd));
692 }
693 pass->EncodeCommands();
694 if (!buffer->SubmitCommands()) {
695 return false;
696 }
697 }
698 return true;
699 };
700 OpenPlaygroundHere(callback);
701 }
702
703 TEST_P(RendererTest, CanGenerateMipmaps) {
704 auto context = GetContext();
705 ASSERT_TRUE(context);
706
707 using VS = MipmapsVertexShader;
708 using FS = MipmapsFragmentShader;
709 auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
710 ASSERT_TRUE(desc.has_value());
711 desc->SetSampleCount(SampleCount::kCount4);
712 desc->SetStencilAttachmentDescriptors(std::nullopt);
713 auto mipmaps_pipeline =
714 context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
715 ASSERT_TRUE(mipmaps_pipeline);
716
717 auto boston = CreateTextureForFixture("boston.jpg", true);
718 ASSERT_TRUE(boston);
719
720 // Vertex buffer.
721 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
722 vertex_builder.SetLabel("Box");
723 auto size = Point(boston->GetSize());
724 vertex_builder.AddVertices({
725 {{0, 0}, {0.0, 0.0}}, // 1
726 {{size.x, 0}, {1.0, 0.0}}, // 2
727 {{size.x, size.y}, {1.0, 1.0}}, // 3
728 {{0, 0}, {0.0, 0.0}}, // 1
729 {{size.x, size.y}, {1.0, 1.0}}, // 3
730 {{0, size.y}, {0.0, 1.0}}, // 4
731 });
732 auto vertex_buffer =
733 vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
734 ASSERT_TRUE(vertex_buffer);
735
736 bool first_frame = true;
737 Renderer::RenderCallback callback = [&](RenderTarget& render_target) {
738 const char* mip_filter_names[] = {"Nearest", "Linear"};
739 const MipFilter mip_filters[] = {MipFilter::kNearest, MipFilter::kLinear};
740 const char* min_filter_names[] = {"Nearest", "Linear"};
741 const MinMagFilter min_filters[] = {MinMagFilter::kNearest,
742 MinMagFilter::kLinear};
743
744 // UI state.
745 static int selected_mip_filter = 1;
746 static int selected_min_filter = 0;
747 static float lod = 4.5;
748
749 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
750 ImGui::Combo("Mip filter", &selected_mip_filter, mip_filter_names,
751 sizeof(mip_filter_names) / sizeof(char*));
752 ImGui::Combo("Min filter", &selected_min_filter, min_filter_names,
753 sizeof(min_filter_names) / sizeof(char*));
754 ImGui::SliderFloat("LOD", &lod, 0, boston->GetMipCount() - 1);
755 ImGui::End();
756
757 auto buffer = context->CreateCommandBuffer();
758 if (!buffer) {
759 return false;
760 }
761 buffer->SetLabel("Playground Command Buffer");
762
763 if (first_frame) {
764 auto pass = buffer->CreateBlitPass();
765 if (!pass) {
766 return false;
767 }
768 pass->SetLabel("Playground Blit Pass");
769
770 pass->GenerateMipmap(boston, "Boston Mipmap");
771
772 pass->EncodeCommands(context->GetResourceAllocator());
773 }
774
775 first_frame = false;
776
777 {
778 auto pass = buffer->CreateRenderPass(render_target);
779 if (!pass) {
780 return false;
781 }
782 pass->SetLabel("Playground Render Pass");
783 {
784 Command cmd;
785 DEBUG_COMMAND_INFO(cmd, "Image LOD");
786 cmd.pipeline = mipmaps_pipeline;
787
788 cmd.BindVertices(vertex_buffer);
789
790 VS::FrameInfo frame_info;
791 frame_info.mvp = Matrix::MakeOrthographic(pass->GetRenderTargetSize()) *
792 Matrix::MakeScale(GetContentScale());
793 VS::BindFrameInfo(
794 cmd, pass->GetTransientsBuffer().EmplaceUniform(frame_info));
795
796 FS::FragInfo frag_info;
797 frag_info.lod = lod;
798 FS::BindFragInfo(cmd,
799 pass->GetTransientsBuffer().EmplaceUniform(frag_info));
800
801 SamplerDescriptor sampler_desc;
802 sampler_desc.mip_filter = mip_filters[selected_mip_filter];
803 sampler_desc.min_filter = min_filters[selected_min_filter];
804 auto sampler = context->GetSamplerLibrary()->GetSampler(sampler_desc);
805 FS::BindTex(cmd, boston, sampler);
806
807 pass->AddCommand(std::move(cmd));
808 }
809 pass->EncodeCommands();
810 }
811
812 if (!buffer->SubmitCommands()) {
813 return false;
814 }
815 return true;
816 };
817 OpenPlaygroundHere(callback);
818 }
819
820 TEST_P(RendererTest, TheImpeller) {
821 using VS = ImpellerVertexShader;
822 using FS = ImpellerFragmentShader;
823
824 auto context = GetContext();
825 auto pipeline_descriptor =
826 PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
827 ASSERT_TRUE(pipeline_descriptor.has_value());
828 pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
829 pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
830 auto pipeline =
831 context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
832 ASSERT_TRUE(pipeline && pipeline->IsValid());
833
834 auto blue_noise = CreateTextureForFixture("blue_noise.png");
835 SamplerDescriptor noise_sampler_desc;
836 noise_sampler_desc.width_address_mode = SamplerAddressMode::kRepeat;
837 noise_sampler_desc.height_address_mode = SamplerAddressMode::kRepeat;
838 auto noise_sampler =
839 context->GetSamplerLibrary()->GetSampler(noise_sampler_desc);
840
841 auto cube_map = CreateTextureCubeForFixture(
842 {"table_mountain_px.png", "table_mountain_nx.png",
843 "table_mountain_py.png", "table_mountain_ny.png",
844 "table_mountain_pz.png", "table_mountain_nz.png"});
845 auto cube_map_sampler = context->GetSamplerLibrary()->GetSampler({});
846
847 SinglePassCallback callback = [&](RenderPass& pass) {
848 auto size = pass.GetRenderTargetSize();
849
850 Command cmd;
851 cmd.pipeline = pipeline;
852 DEBUG_COMMAND_INFO(cmd, "Impeller SDF scene");
853 VertexBufferBuilder<VS::PerVertexData> builder;
854 builder.AddVertices({{Point()},
855 {Point(0, size.height)},
856 {Point(size.width, 0)},
857 {Point(size.width, 0)},
858 {Point(0, size.height)},
859 {Point(size.width, size.height)}});
860 cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
861
862 VS::FrameInfo frame_info;
863 frame_info.mvp = Matrix::MakeOrthographic(size);
864 VS::BindFrameInfo(cmd,
865 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
866
867 FS::FragInfo fs_uniform;
868 fs_uniform.texture_size = Point(size);
869 fs_uniform.time = GetSecondsElapsed();
870 FS::BindFragInfo(cmd,
871 pass.GetTransientsBuffer().EmplaceUniform(fs_uniform));
872 FS::BindBlueNoise(cmd, blue_noise, noise_sampler);
873 FS::BindCubeMap(cmd, cube_map, cube_map_sampler);
874
875 pass.AddCommand(std::move(cmd));
876 return true;
877 };
878 OpenPlaygroundHere(callback);
879 }
880
881 TEST_P(RendererTest, ArrayUniforms) {
882 using VS = ArrayVertexShader;
883 using FS = ArrayFragmentShader;
884
885 auto context = GetContext();
886 auto pipeline_descriptor =
887 PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
888 ASSERT_TRUE(pipeline_descriptor.has_value());
889 pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
890 pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
891 auto pipeline =
892 context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
893 ASSERT_TRUE(pipeline && pipeline->IsValid());
894
895 SinglePassCallback callback = [&](RenderPass& pass) {
896 auto size = pass.GetRenderTargetSize();
897
898 Command cmd;
899 cmd.pipeline = pipeline;
900 DEBUG_COMMAND_INFO(cmd, "Google Dots");
901 VertexBufferBuilder<VS::PerVertexData> builder;
902 builder.AddVertices({{Point()},
903 {Point(0, size.height)},
904 {Point(size.width, 0)},
905 {Point(size.width, 0)},
906 {Point(0, size.height)},
907 {Point(size.width, size.height)}});
908 cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
909
910 VS::FrameInfo frame_info;
911 frame_info.mvp =
912 Matrix::MakeOrthographic(size) * Matrix::MakeScale(GetContentScale());
913 VS::BindFrameInfo(cmd,
914 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
915
916 auto time = GetSecondsElapsed();
917 auto y_pos = [&time](float x) {
918 return 400 + 10 * std::cos(time * 5 + x / 6);
919 };
920
921 FS::FragInfo fs_uniform = {
922 .circle_positions = {Point(430, y_pos(0)), Point(480, y_pos(1)),
923 Point(530, y_pos(2)), Point(580, y_pos(3))},
924 .colors = {Color::MakeRGBA8(66, 133, 244, 255),
925 Color::MakeRGBA8(219, 68, 55, 255),
926 Color::MakeRGBA8(244, 180, 0, 255),
927 Color::MakeRGBA8(15, 157, 88, 255)},
928 };
929 FS::BindFragInfo(cmd,
930 pass.GetTransientsBuffer().EmplaceUniform(fs_uniform));
931
932 pass.AddCommand(std::move(cmd));
933 return true;
934 };
935 OpenPlaygroundHere(callback);
936 }
937
938 TEST_P(RendererTest, InactiveUniforms) {
939 using VS = InactiveUniformsVertexShader;
940 using FS = InactiveUniformsFragmentShader;
941
942 auto context = GetContext();
943 auto pipeline_descriptor =
944 PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
945 ASSERT_TRUE(pipeline_descriptor.has_value());
946 pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
947 pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
948 auto pipeline =
949 context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
950 ASSERT_TRUE(pipeline && pipeline->IsValid());
951
952 SinglePassCallback callback = [&](RenderPass& pass) {
953 auto size = pass.GetRenderTargetSize();
954
955 Command cmd;
956 cmd.pipeline = pipeline;
957 DEBUG_COMMAND_INFO(cmd, "Inactive Uniform");
958 VertexBufferBuilder<VS::PerVertexData> builder;
959 builder.AddVertices({{Point()},
960 {Point(0, size.height)},
961 {Point(size.width, 0)},
962 {Point(size.width, 0)},
963 {Point(0, size.height)},
964 {Point(size.width, size.height)}});
965 cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
966
967 VS::FrameInfo frame_info;
968 frame_info.mvp =
969 Matrix::MakeOrthographic(size) * Matrix::MakeScale(GetContentScale());
970 VS::BindFrameInfo(cmd,
971 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
972
973 FS::FragInfo fs_uniform = {.unused_color = Color::Red(),
974 .color = Color::Green()};
975 FS::BindFragInfo(cmd,
976 pass.GetTransientsBuffer().EmplaceUniform(fs_uniform));
977
978 pass.AddCommand(std::move(cmd));
979 return true;
980 };
981 OpenPlaygroundHere(callback);
982 }
983
984 TEST_P(RendererTest, CanCreateCPUBackedTexture) {
985 if (GetParam() == PlaygroundBackend::kOpenGLES) {
986 GTEST_SKIP_("CPU backed textures are not supported on OpenGLES.");
987 }
988
989 auto context = GetContext();
990 auto allocator = context->GetResourceAllocator();
991 size_t dimension = 2;
992
993 do {
994 ISize size(dimension, dimension);
995 TextureDescriptor texture_descriptor;
996 texture_descriptor.storage_mode = StorageMode::kHostVisible;
997 texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
998 texture_descriptor.size = size;
999 auto row_bytes =
1000 std::max(static_cast<uint16_t>(size.width * 4),
1001 allocator->MinimumBytesPerRow(texture_descriptor.format));
1002 auto buffer_size = size.height * row_bytes;
1003
1004 DeviceBufferDescriptor buffer_descriptor;
1005 buffer_descriptor.storage_mode = StorageMode::kHostVisible;
1006 buffer_descriptor.size = buffer_size;
1007
1008 auto buffer = allocator->CreateBuffer(buffer_descriptor);
1009
1010 ASSERT_TRUE(buffer);
1011
1012 auto texture = buffer->AsTexture(*allocator, texture_descriptor, row_bytes);
1013
1014 ASSERT_TRUE(texture);
1015 ASSERT_TRUE(texture->IsValid());
1016
1017 dimension *= 2;
1018 } while (dimension <= 8192);
1019 }
1020
1021 TEST_P(RendererTest, DefaultIndexSize) {
1022 using VS = BoxFadeVertexShader;
1023
1024 // Default to 16bit index buffer size, as this is a reasonable default and
1025 // supported on all backends without extensions.
1026 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
1027 vertex_builder.AppendIndex(0u);
1028 ASSERT_EQ(vertex_builder.GetIndexType(), IndexType::k16bit);
1029 }
1030
1031 TEST_P(RendererTest, DefaultIndexBehavior) {
1032 using VS = BoxFadeVertexShader;
1033
1034 // Do not create any index buffer if no indices were provided.
1035 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
1036 ASSERT_EQ(vertex_builder.GetIndexType(), IndexType::kNone);
1037 }
1038
1039 TEST_P(RendererTest, VertexBufferBuilder) {
1040 // Does not create index buffer if one is provided.
1041 using VS = BoxFadeVertexShader;
1042 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
1043 vertex_builder.SetLabel("Box");
1044 vertex_builder.AddVertices({
1045 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1046 {{800, 100, 0.0}, {1.0, 0.0}}, // 2
1047 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1048 {{100, 800, 0.0}, {0.0, 1.0}}, // 4
1049 });
1050 vertex_builder.AppendIndex(0);
1051 vertex_builder.AppendIndex(1);
1052 vertex_builder.AppendIndex(2);
1053 vertex_builder.AppendIndex(1);
1054 vertex_builder.AppendIndex(2);
1055 vertex_builder.AppendIndex(3);
1056
1057 ASSERT_EQ(vertex_builder.GetIndexCount(), 6u);
1058 ASSERT_EQ(vertex_builder.GetVertexCount(), 4u);
1059 }
1060
1061 class CompareFunctionUIData {
1062 public:
1063 CompareFunctionUIData() {
1064 labels_.push_back("Never");
1065 functions_.push_back(CompareFunction::kNever);
1066 labels_.push_back("Always");
1067 functions_.push_back(CompareFunction::kAlways);
1068 labels_.push_back("Less");
1069 functions_.push_back(CompareFunction::kLess);
1070 labels_.push_back("Equal");
1071 functions_.push_back(CompareFunction::kEqual);
1072 labels_.push_back("LessEqual");
1073 functions_.push_back(CompareFunction::kLessEqual);
1074 labels_.push_back("Greater");
1075 functions_.push_back(CompareFunction::kGreater);
1076 labels_.push_back("NotEqual");
1077 functions_.push_back(CompareFunction::kNotEqual);
1078 labels_.push_back("GreaterEqual");
1079 functions_.push_back(CompareFunction::kGreaterEqual);
1080 assert(labels_.size() == functions_.size());
1081 }
1082
1083 const char* const* labels() const { return &labels_[0]; }
1084
1085 int size() const { return labels_.size(); }
1086
1087 int IndexOf(CompareFunction func) const {
1088 for (size_t i = 0; i < functions_.size(); i++) {
1089 if (functions_[i] == func) {
1090 return i;
1091 }
1092 }
1093 FML_UNREACHABLE();
1094 return -1;
1095 }
1096
1097 CompareFunction FunctionOf(int index) const { return functions_[index]; }
1098
1099 private:
1100 std::vector<const char*> labels_;
1101 std::vector<CompareFunction> functions_;
1102 };
1103
1104 static const CompareFunctionUIData& CompareFunctionUI() {
1105 static CompareFunctionUIData data;
1106 return data;
1107 }
1108
1109 TEST_P(RendererTest, StencilMask) {
1110 using VS = BoxFadeVertexShader;
1111 using FS = BoxFadeFragmentShader;
1112 auto context = GetContext();
1113 ASSERT_TRUE(context);
1114 using BoxFadePipelineBuilder = PipelineBuilder<VS, FS>;
1115 auto desc = BoxFadePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
1116 ASSERT_TRUE(desc.has_value());
1117
1118 // Vertex buffer.
1119 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
1120 vertex_builder.SetLabel("Box");
1121 vertex_builder.AddVertices({
1122 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1123 {{800, 100, 0.0}, {1.0, 0.0}}, // 2
1124 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1125 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1126 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1127 {{100, 800, 0.0}, {0.0, 1.0}}, // 4
1128 });
1129 auto vertex_buffer =
1130 vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
1131 ASSERT_TRUE(vertex_buffer);
1132
1133 desc->SetSampleCount(SampleCount::kCount4);
1134 desc->SetStencilAttachmentDescriptors(std::nullopt);
1135
1136 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
1137 auto boston = CreateTextureForFixture("boston.jpg");
1138 ASSERT_TRUE(bridge && boston);
1139 auto sampler = context->GetSamplerLibrary()->GetSampler({});
1140 ASSERT_TRUE(sampler);
1141
1142 static bool mirror = false;
1143 static int stencil_reference_write = 0xFF;
1144 static int stencil_reference_read = 0x1;
1145 std::vector<uint8_t> stencil_contents;
1146 static int last_stencil_contents_reference_value = 0;
1147 static int current_front_compare =
1148 CompareFunctionUI().IndexOf(CompareFunction::kLessEqual);
1149 static int current_back_compare =
1150 CompareFunctionUI().IndexOf(CompareFunction::kLessEqual);
1151 Renderer::RenderCallback callback = [&](RenderTarget& render_target) {
1152 auto buffer = context->CreateCommandBuffer();
1153 if (!buffer) {
1154 return false;
1155 }
1156 buffer->SetLabel("Playground Command Buffer");
1157
1158 {
1159 // Configure the stencil attachment for the test.
1160 RenderTarget::AttachmentConfig stencil_config;
1161 stencil_config.load_action = LoadAction::kLoad;
1162 stencil_config.store_action = StoreAction::kDontCare;
1163 stencil_config.storage_mode = StorageMode::kHostVisible;
1164 auto render_target_allocator =
1165 RenderTargetAllocator(context->GetResourceAllocator());
1166 render_target.SetupStencilAttachment(*context, render_target_allocator,
1167 render_target.GetRenderTargetSize(),
1168 true, "stencil", stencil_config);
1169 // Fill the stencil buffer with an checkerboard pattern.
1170 const auto target_width = render_target.GetRenderTargetSize().width;
1171 const auto target_height = render_target.GetRenderTargetSize().height;
1172 const size_t target_size = target_width * target_height;
1173 if (stencil_contents.size() != target_size ||
1174 last_stencil_contents_reference_value != stencil_reference_write) {
1175 stencil_contents.resize(target_size);
1176 last_stencil_contents_reference_value = stencil_reference_write;
1177 for (int y = 0; y < target_height; y++) {
1178 for (int x = 0; x < target_width; x++) {
1179 const auto index = y * target_width + x;
1180 const auto kCheckSize = 64;
1181 const auto value =
1182 (((y / kCheckSize) + (x / kCheckSize)) % 2 == 0) *
1183 stencil_reference_write;
1184 stencil_contents[index] = value;
1185 }
1186 }
1187 }
1188 if (!render_target.GetStencilAttachment()->texture->SetContents(
1189 stencil_contents.data(), stencil_contents.size(), 0, false)) {
1190 VALIDATION_LOG << "Could not upload stencil contents to device memory";
1191 return false;
1192 }
1193 auto pass = buffer->CreateRenderPass(render_target);
1194 if (!pass) {
1195 return false;
1196 }
1197 pass->SetLabel("Stencil Buffer");
1198 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1199 ImGui::SliderInt("Stencil Write Value", &stencil_reference_write, 0,
1200 0xFF);
1201 ImGui::SliderInt("Stencil Compare Value", &stencil_reference_read, 0,
1202 0xFF);
1203 ImGui::Checkbox("Back face mode", &mirror);
1204 ImGui::ListBox("Front face compare function", &current_front_compare,
1205 CompareFunctionUI().labels(), CompareFunctionUI().size());
1206 ImGui::ListBox("Back face compare function", &current_back_compare,
1207 CompareFunctionUI().labels(), CompareFunctionUI().size());
1208 ImGui::End();
1209
1210 StencilAttachmentDescriptor front;
1211 front.stencil_compare =
1212 CompareFunctionUI().FunctionOf(current_front_compare);
1213 StencilAttachmentDescriptor back;
1214 back.stencil_compare =
1215 CompareFunctionUI().FunctionOf(current_back_compare);
1216 desc->SetStencilAttachmentDescriptors(front, back);
1217 auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get();
1218
1219 assert(pipeline && pipeline->IsValid());
1220
1221 Command cmd;
1222 DEBUG_COMMAND_INFO(cmd, "Box");
1223 cmd.pipeline = pipeline;
1224 cmd.stencil_reference = stencil_reference_read;
1225
1226 cmd.BindVertices(vertex_buffer);
1227
1228 VS::UniformBuffer uniforms;
1229 uniforms.mvp = Matrix::MakeOrthographic(pass->GetRenderTargetSize()) *
1230 Matrix::MakeScale(GetContentScale());
1231 if (mirror) {
1232 uniforms.mvp = Matrix::MakeScale(Vector2(-1, 1)) * uniforms.mvp;
1233 }
1234 VS::BindUniformBuffer(
1235 cmd, pass->GetTransientsBuffer().EmplaceUniform(uniforms));
1236
1237 FS::FrameInfo frame_info;
1238 frame_info.current_time = GetSecondsElapsed();
1239 frame_info.cursor_position = GetCursorPosition();
1240 frame_info.window_size.x = GetWindowSize().width;
1241 frame_info.window_size.y = GetWindowSize().height;
1242
1243 FS::BindFrameInfo(cmd,
1244 pass->GetTransientsBuffer().EmplaceUniform(frame_info));
1245 FS::BindContents1(cmd, boston, sampler);
1246 FS::BindContents2(cmd, bridge, sampler);
1247 if (!pass->AddCommand(std::move(cmd))) {
1248 return false;
1249 }
1250 pass->EncodeCommands();
1251 }
1252
1253 if (!buffer->SubmitCommands()) {
1254 return false;
1255 }
1256 return true;
1257 };
1258 OpenPlaygroundHere(callback);
1259 }
1260
1261 } // namespace testing
1262 } // namespace impeller
1263
1264 // NOLINTEND(bugprone-unchecked-optional-access)
impeller::Color::Blue
static constexpr Color Blue()
Definition: color.h:266
impeller::Command
An object used to specify work to the GPU along with references to resources the GPU will used when d...
Definition: command.h:99
impeller::VertexBuffer::index_type
IndexType index_type
Definition: vertex_buffer.h:18
DEBUG_COMMAND_INFO
#define DEBUG_COMMAND_INFO(obj, arg)
Definition: command.h:31
impeller::Matrix::MakeRotationX
static Matrix MakeRotationX(Radians r)
Definition: matrix.h:181
impeller::RenderTarget::AttachmentConfig::store_action
StoreAction store_action
Definition: render_target.h:53
impeller::testing::CompareFunctionUI
static const CompareFunctionUIData & CompareFunctionUI()
Definition: renderer_unittests.cc:1104
impeller::LoadAction::kLoad
@ kLoad
impeller::RenderTarget::AttachmentConfig::load_action
LoadAction load_action
Definition: render_target.h:52
impeller::TextureUsageMask
uint64_t TextureUsageMask
Definition: formats.h:274
impeller::IndexType::k16bit
@ k16bit
impeller::Attachment::store_action
StoreAction store_action
Definition: formats.h:594
impeller::Scalar
float Scalar
Definition: scalar.h:15
impeller::StencilAttachmentDescriptor::stencil_compare
CompareFunction stencil_compare
Definition: formats.h:550
impeller::CompareFunction::kGreater
@ kGreater
Comparison test passes if new_value > current_value.
sampler_library.h
impeller::testing::CompareFunctionUIData::IndexOf
int IndexOf(CompareFunction func) const
Definition: renderer_unittests.cc:1087
impeller::Degrees::degrees
Scalar degrees
Definition: scalar.h:44
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:262
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:47
impeller::Tessellator
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition: tessellator.h:35
impeller::Color::MakeRGBA8
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition: color.h:152
impeller::testing::CompareFunctionUIData
Definition: renderer_unittests.cc:1061
impeller::DeviceBufferDescriptor
Definition: device_buffer_descriptor.h:13
impeller::Renderer::RenderCallback
std::function< bool(RenderTarget &render_target)> RenderCallback
Definition: renderer.h:25
impeller::VertexBufferBuilder::GetVertexCount
size_t GetVertexCount() const
Definition: vertex_buffer_builder.h:53
impeller::ColorAttachment
Definition: formats.h:599
impeller::CompareFunction::kNotEqual
@ kNotEqual
Comparison test passes if new_value != current_value.
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:42
impeller::RenderTarget::SetColorAttachment
RenderTarget & SetColorAttachment(const ColorAttachment &attachment, size_t index)
Definition: render_target.cc:180
impeller::VertexBuffer
Definition: vertex_buffer.h:12
formats.h
impeller::PathBuilder
Definition: path_builder.h:13
impeller::TextureDescriptor::mip_count
size_t mip_count
Definition: texture_descriptor.h:44
impeller::Vector2
Point Vector2
Definition: point.h:310
impeller::Matrix::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:194
impeller::SampleCount::kCount1
@ kCount1
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
impeller::StoreAction::kDontCare
@ kDontCare
impeller::DeviceBufferDescriptor::size
size_t size
Definition: device_buffer_descriptor.h:15
impeller::VertexBufferBuilder::AddVertices
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
Definition: vertex_buffer_builder.h:64
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
impeller::Color::Yellow
static constexpr Color Yellow()
Definition: color.h:832
impeller::VertexBuffer::vertex_buffer
BufferView vertex_buffer
Definition: vertex_buffer.h:13
impeller::StorageMode::kHostVisible
@ kHostVisible
pipeline_builder.h
impeller::VertexBuffer::vertex_count
size_t vertex_count
Definition: vertex_buffer.h:17
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:94
impeller::Command::instance_count
size_t instance_count
Definition: command.h:177
impeller::TextureDescriptor::usage
TextureUsageMask usage
Definition: texture_descriptor.h:45
impeller::Vector3::x
Scalar x
Definition: vector.h:20
impeller::SampleCount::kCount4
@ kCount4
sampler.h
impeller::PolygonMode::kFill
@ kFill
impeller::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:181
impeller::FillType::kPositive
@ kPositive
impeller::testing::CompareFunctionUIData::CompareFunctionUIData
CompareFunctionUIData()
Definition: renderer_unittests.cc:1063
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:457
impeller::RenderTarget::AttachmentConfig
Definition: render_target.h:50
tessellator.h
path_builder.h
impeller::MinMagFilter::kNearest
@ kNearest
Select nearest to the sample point. Most widely supported.
impeller::SamplerDescriptor
Definition: sampler_descriptor.h:18
impeller::testing::INSTANTIATE_PLAYGROUND_SUITE
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
surface.h
command.h
impeller::StencilAttachment
Definition: formats.h:607
impeller::MipFilter::kNearest
@ kNearest
Sample from the nearest mip level.
impeller::RenderPass::GetRenderTargetSize
ISize GetRenderTargetSize() const
Definition: render_pass.cc:30
impeller::testing::TEST_P
TEST_P(AiksTest, RotateColorFilteredPath)
Definition: aiks_unittests.cc:56
impeller::TSize< int64_t >
impeller::LoadAction::kClear
@ kClear
impeller::SamplerDescriptor::min_filter
MinMagFilter min_filter
Definition: sampler_descriptor.h:19
impeller::StorageMode::kDeviceTransient
@ kDeviceTransient
impeller::Point
TPoint< Scalar > Point
Definition: point.h:306
impeller::CullMode::kBackFace
@ kBackFace
impeller::testing::CompareFunctionUIData::FunctionOf
CompareFunction FunctionOf(int index) const
Definition: renderer_unittests.cc:1097
impeller::MipFilter
MipFilter
Definition: formats.h:369
impeller::Attachment::texture
std::shared_ptr< Texture > texture
Definition: formats.h:591
impeller::VertexBufferBuilder
Definition: vertex_buffer_builder.h:23
impeller::MinMagFilter::kLinear
@ kLinear
impeller::BufferView::buffer
std::shared_ptr< const Buffer > buffer
Definition: buffer_view.h:14
impeller::DeviceBufferDescriptor::storage_mode
StorageMode storage_mode
Definition: device_buffer_descriptor.h:14
impeller::Color::Random
static Color Random()
Definition: color.h:840
impeller::SamplerDescriptor::width_address_mode
SamplerAddressMode width_address_mode
Definition: sampler_descriptor.h:23
impeller::TextureUsage::kShaderRead
@ kShaderRead
impeller::CompareFunction::kNever
@ kNever
Comparison test never passes.
impeller::WindingOrder::kCounterClockwise
@ kCounterClockwise
impeller::Vector3::z
Scalar z
Definition: vector.h:22
impeller::VertexBufferBuilder::GetIndexCount
size_t GetIndexCount() const
Definition: vertex_buffer_builder.h:55
impeller::Radians
Definition: scalar.h:35
impeller::IndexType::kNone
@ kNone
Does not use the index buffer.
impeller::Tessellator::Result::kSuccess
@ kSuccess
impeller::RenderTarget::AttachmentConfig::storage_mode
StorageMode storage_mode
Definition: render_target.h:51
impeller::MinMagFilter
MinMagFilter
Definition: formats.h:361
impeller::testing::CompareFunctionUIData::size
int size() const
Definition: renderer_unittests.cc:1085
impeller::RenderTarget
Definition: render_target.h:48
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:264
impeller::StoreAction::kStore
@ kStore
strings.h
impeller::Vector3::y
Scalar y
Definition: vector.h:21
impeller::RenderTargetAllocator
a wrapper around the impeller [Allocator] instance that can be used to provide caching of allocated r...
Definition: render_target.h:22
impeller::VertexBufferBuilder::AppendIndex
VertexBufferBuilder & AppendIndex(IndexType_ index)
Definition: vertex_buffer_builder.h:73
impeller::TSize::width
Type width
Definition: size.h:21
impeller::SamplerDescriptor::mip_filter
MipFilter mip_filter
Definition: sampler_descriptor.h:21
impeller::VertexBufferBuilder::CreateVertexBuffer
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
Definition: vertex_buffer_builder.h:78
pipeline_library.h
impeller::CompareFunction::kLess
@ kLess
Comparison test passes if new_value < current_value.
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:27
decompressed_image.h
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:43
sampler_descriptor.h
impeller::VertexBuffer::index_buffer
BufferView index_buffer
Definition: vertex_buffer.h:14
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:60
command_buffer.h
impeller::Command::stencil_reference
uint32_t stencil_reference
Definition: command.h:152
impeller::VertexBufferBuilder::GetIndexType
constexpr impeller::IndexType GetIndexType() const
Definition: vertex_buffer_builder.h:32
impeller::CompareFunction::kGreaterEqual
@ kGreaterEqual
Comparison test passes if new_value >= current_value.
impeller::PlaygroundBackend::kOpenGLES
@ kOpenGLES
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:208
impeller::Tessellator::Tessellate
Tessellator::Result Tessellate(FillType fill_type, const Path::Polyline &polyline, const BuilderCallback &callback) const
Generates filled triangles from the polyline. A callback is invoked once for the entire tessellation.
Definition: tessellator.cc:61
impeller::Range
Definition: range.h:13
impeller::CompareFunction
CompareFunction
Definition: formats.h:486
impeller::PipelineBuilder::MakeDefaultPipelineDescriptor
static std::optional< PipelineDescriptor > MakeDefaultPipelineDescriptor(const Context &context)
Create a default pipeline descriptor using the combination reflected shader information....
Definition: pipeline_builder.h:50
impeller::CompareFunction::kAlways
@ kAlways
Comparison test passes always passes.
impeller::RenderTarget::SetStencilAttachment
RenderTarget & SetStencilAttachment(std::optional< StencilAttachment > attachment)
Definition: render_target.cc:199
impeller::VertexBufferBuilder::SetLabel
void SetLabel(std::string label)
Definition: vertex_buffer_builder.h:45
impeller::PixelFormat::kS8UInt
@ kS8UInt
impeller::StencilAttachmentDescriptor
Definition: formats.h:544
impeller::Attachment::load_action
LoadAction load_action
Definition: formats.h:593
impeller::MipFilter::kLinear
@ kLinear
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:43
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:448
impeller::VertexBufferBuilder::AppendVertex
VertexBufferBuilder & AppendVertex(VertexType_ vertex)
Definition: vertex_buffer_builder.h:59
impeller::Attachment::IsValid
bool IsValid() const
Definition: formats.cc:26
impeller::Degrees
Definition: scalar.h:43
impeller::CompareFunction::kEqual
@ kEqual
Comparison test passes if new_value == current_value.
impeller::PolygonMode::kLine
@ kLine
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:40
impeller::TSize::height
Type height
Definition: size.h:22
impeller::HostBuffer::EmplaceStorageBuffer
BufferView EmplaceStorageBuffer(const StorageBufferType &buffer)
Emplace storage buffer data onto the host buffer. Ensure that backend specific uniform alignment requ...
Definition: host_buffer.h:65
impeller::PlaygroundTest
Definition: playground_test.h:22
device_buffer_descriptor.h
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:39
impeller::Command::BindVertices
bool BindVertices(const VertexBuffer &buffer)
Specify the vertex and index buffer to use for this command.
Definition: command.cc:15
impeller::Command::pipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > pipeline
Definition: command.h:103
impeller::PipelineBuilder
An optional (but highly recommended) utility for creating pipelines from reflected shader information...
Definition: pipeline_builder.h:31
renderer.h
impeller::SamplerDescriptor::height_address_mode
SamplerAddressMode height_address_mode
Definition: sampler_descriptor.h:24
impeller::CompareFunction::kLessEqual
@ kLessEqual
Comparison test passes if new_value <= current_value.
impeller::TextureUsage::kShaderWrite
@ kShaderWrite
impeller::RenderPass::AddCommand
bool AddCommand(Command &&command)
Record a command for subsequent encoding to the underlying command buffer. No work is encoded into th...
Definition: render_pass.cc:46
impeller
Definition: aiks_context.cc:10
playground_test.h
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:103
impeller::testing::CompareFunctionUIData::labels
const char *const * labels() const
Definition: renderer_unittests.cc:1083
compressed_image.h
impeller::RenderPass::GetTransientsBuffer
HostBuffer & GetTransientsBuffer()
Definition: render_pass.cc:34
impeller::Vector3
Definition: vector.h:17
vertex_buffer_builder.h
impeller::SamplerAddressMode::kRepeat
@ kRepeat