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