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/fml/time/time_point.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/planet.frag.h"
27 #include "impeller/fixtures/planet.vert.h"
28 #include "impeller/fixtures/sepia.frag.h"
29 #include "impeller/fixtures/sepia.vert.h"
30 #include "impeller/fixtures/swizzle.frag.h"
31 #include "impeller/fixtures/texture.frag.h"
32 #include "impeller/fixtures/texture.vert.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 {
47 std::pair<std::shared_ptr<impeller::HostBuffer>,
48  std::shared_ptr<impeller::HostBuffer>>
49 createHostBuffers(const std::shared_ptr<impeller::Context>& context) {
50  auto data_host_buffer = impeller::HostBuffer::Create(
51  context->GetResourceAllocator(), context->GetIdleWaiter(),
52  context->GetCapabilities()->GetMinimumUniformAlignment());
53  auto indexes_host_buffer =
54  context->GetCapabilities()->NeedsPartitionedHostBuffer()
56  context->GetResourceAllocator(), context->GetIdleWaiter(),
57  context->GetCapabilities()->GetMinimumUniformAlignment())
58  : data_host_buffer;
59  return {data_host_buffer, indexes_host_buffer};
60 }
61 } // namespace
62 
63 namespace impeller {
64 namespace testing {
65 
66 using RendererTest = PlaygroundTest;
68 
69 TEST_P(RendererTest, CanCreateBoxPrimitive) {
70  using VS = BoxFadeVertexShader;
71  using FS = BoxFadeFragmentShader;
72  auto context = GetContext();
73  ASSERT_TRUE(context);
74  using BoxPipelineBuilder = PipelineBuilder<VS, FS>;
75  auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context);
76  ASSERT_TRUE(desc.has_value());
77  desc->SetSampleCount(SampleCount::kCount4);
78  desc->SetStencilAttachmentDescriptors(std::nullopt);
79 
80  // Vertex buffer.
82  vertex_builder.SetLabel("Box");
83  vertex_builder.AddVertices({
84  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
85  {{800, 100, 0.0}, {1.0, 0.0}}, // 2
86  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
87  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
88  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
89  {{100, 800, 0.0}, {0.0, 1.0}}, // 4
90  });
91  auto bridge = CreateTextureForFixture("bay_bridge.jpg");
92  auto boston = CreateTextureForFixture("boston.jpg");
93  ASSERT_TRUE(bridge && boston);
94  raw_ptr<const Sampler> sampler = context->GetSamplerLibrary()->GetSampler({});
95  ASSERT_TRUE(sampler);
96 
97  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
98  SinglePassCallback callback = [&](RenderPass& pass) {
99  ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
100  static bool wireframe;
101  ImGui::Checkbox("Wireframe", &wireframe);
102  ImGui::End();
103 
104  desc->SetPolygonMode(wireframe ? PolygonMode::kLine : PolygonMode::kFill);
105  auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get();
106 
107  assert(pipeline && pipeline->IsValid());
108 
109  pass.SetCommandLabel("Box");
110  pass.SetPipeline(pipeline);
111  pass.SetVertexBuffer(
112  vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator()));
113 
114  VS::UniformBuffer uniforms;
115  EXPECT_EQ(pass.GetOrthographicTransform(),
116  Matrix::MakeOrthographic(pass.GetRenderTargetSize()));
117  uniforms.mvp =
118  pass.GetOrthographicTransform() * Matrix::MakeScale(GetContentScale());
119  VS::BindUniformBuffer(pass, data_host_buffer->EmplaceUniform(uniforms));
120 
121  FS::FrameInfo frame_info;
122  frame_info.current_time = GetSecondsElapsed();
123  frame_info.cursor_position = GetCursorPosition();
124  frame_info.window_size.x = GetWindowSize().width;
125  frame_info.window_size.y = GetWindowSize().height;
126 
127  FS::BindFrameInfo(pass, data_host_buffer->EmplaceUniform(frame_info));
128  FS::BindContents1(pass, boston, sampler);
129  FS::BindContents2(pass, bridge, sampler);
130 
131  data_host_buffer->Reset();
132  return pass.Draw().ok();
133  };
134  OpenPlaygroundHere(callback);
135 }
136 
137 TEST_P(RendererTest, CanRenderPerspectiveCube) {
138  using VS = ColorsVertexShader;
139  using FS = ColorsFragmentShader;
140  auto context = GetContext();
141  ASSERT_TRUE(context);
143  ASSERT_TRUE(desc.has_value());
144  desc->SetCullMode(CullMode::kBackFace);
145  desc->SetWindingOrder(WindingOrder::kCounterClockwise);
146  desc->SetSampleCount(SampleCount::kCount4);
147  desc->ClearStencilAttachments();
148 
149  // Setup the vertex layout to take two bindings. The first for positions and
150  // the second for colors.
151  auto vertex_desc = std::make_shared<VertexDescriptor>();
152  ShaderStageIOSlot position_slot = VS::kInputPosition;
153  ShaderStageIOSlot color_slot = VS::kInputColor;
154  position_slot.binding = 0;
155  position_slot.offset = 0;
156  color_slot.binding = 1;
157  color_slot.offset = 0;
158  const std::vector<ShaderStageIOSlot> io_slots = {position_slot, color_slot};
159  const std::vector<ShaderStageBufferLayout> layouts = {
160  ShaderStageBufferLayout{.stride = 12u, .binding = 0},
161  ShaderStageBufferLayout{.stride = 16u, .binding = 1}};
162  vertex_desc->RegisterDescriptorSetLayouts(VS::kDescriptorSetLayouts);
163  vertex_desc->RegisterDescriptorSetLayouts(FS::kDescriptorSetLayouts);
164  vertex_desc->SetStageInputs(io_slots, layouts);
165  desc->SetVertexDescriptor(std::move(vertex_desc));
166  auto pipeline =
167  context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
168  ASSERT_TRUE(pipeline);
169 
170  struct Cube {
171  Vector3 positions[8] = {
172  // -Z
173  {-1, -1, -1},
174  {1, -1, -1},
175  {1, 1, -1},
176  {-1, 1, -1},
177  // +Z
178  {-1, -1, 1},
179  {1, -1, 1},
180  {1, 1, 1},
181  {-1, 1, 1},
182  };
183  Color colors[8] = {
186  };
187  uint16_t indices[36] = {
188  1, 5, 2, 2, 5, 6, // +X
189  4, 0, 7, 7, 0, 3, // -X
190  4, 5, 0, 0, 5, 1, // +Y
191  3, 2, 7, 7, 2, 6, // -Y
192  5, 4, 6, 6, 4, 7, // +Z
193  0, 1, 3, 3, 1, 2, // -Z
194  };
195  } cube;
196 
197  auto device_buffer = context->GetResourceAllocator()->CreateBufferWithCopy(
198  reinterpret_cast<uint8_t*>(&cube), sizeof(cube));
199 
200  raw_ptr<const Sampler> sampler = context->GetSamplerLibrary()->GetSampler({});
201  ASSERT_TRUE(sampler);
202 
203  Vector3 euler_angles;
204  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
205  SinglePassCallback callback = [&](RenderPass& pass) {
206  static Degrees fov_y(60);
207  static Scalar distance = 10;
208 
209  ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
210  ImGui::SliderFloat("Field of view", &fov_y.degrees, 0, 180);
211  ImGui::SliderFloat("Camera distance", &distance, 0, 30);
212  ImGui::End();
213 
214  pass.SetCommandLabel("Perspective Cube");
215  pass.SetPipeline(pipeline);
216 
217  std::array<BufferView, 2> vertex_buffers = {
218  BufferView(device_buffer,
219  Range(offsetof(Cube, positions), sizeof(Cube::positions))),
220  BufferView(device_buffer,
221  Range(offsetof(Cube, colors), sizeof(Cube::colors))),
222  };
223 
224  BufferView index_buffer(
225  device_buffer, Range(offsetof(Cube, indices), sizeof(Cube::indices)));
226  pass.SetVertexBuffer(vertex_buffers.data(), vertex_buffers.size());
227  pass.SetElementCount(36);
228  pass.SetIndexBuffer(index_buffer, IndexType::k16bit);
229 
230  VS::UniformBuffer uniforms;
231  Scalar time = GetSecondsElapsed();
232  euler_angles = Vector3(0.19 * time, 0.7 * time, 0.43 * time);
233 
234  uniforms.mvp =
235  Matrix::MakePerspective(fov_y, pass.GetRenderTargetSize(), 0, 10) *
237  Matrix::MakeRotationX(Radians(euler_angles.x)) *
238  Matrix::MakeRotationY(Radians(euler_angles.y)) *
239  Matrix::MakeRotationZ(Radians(euler_angles.z));
240  VS::BindUniformBuffer(pass, data_host_buffer->EmplaceUniform(uniforms));
241 
242  data_host_buffer->Reset();
243  return pass.Draw().ok();
244  };
245  OpenPlaygroundHere(callback);
246 }
247 
248 TEST_P(RendererTest, CanRenderMultiplePrimitives) {
249  using VS = BoxFadeVertexShader;
250  using FS = BoxFadeFragmentShader;
251  auto context = GetContext();
252  ASSERT_TRUE(context);
253  using BoxPipelineBuilder = PipelineBuilder<VS, FS>;
254  auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context);
255  ASSERT_TRUE(desc.has_value());
256  desc->SetSampleCount(SampleCount::kCount4);
257  desc->SetStencilAttachmentDescriptors(std::nullopt);
258  auto box_pipeline =
259  context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
260  ASSERT_TRUE(box_pipeline);
261 
262  // Vertex buffer.
264  vertex_builder.SetLabel("Box");
265  vertex_builder.AddVertices({
266  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
267  {{800, 100, 0.0}, {1.0, 0.0}}, // 2
268  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
269  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
270  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
271  {{100, 800, 0.0}, {0.0, 1.0}}, // 4
272  });
273  auto vertex_buffer =
274  vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
275  ASSERT_TRUE(vertex_buffer);
276 
277  auto bridge = CreateTextureForFixture("bay_bridge.jpg");
278  auto boston = CreateTextureForFixture("boston.jpg");
279  ASSERT_TRUE(bridge && boston);
280  raw_ptr<const Sampler> sampler = context->GetSamplerLibrary()->GetSampler({});
281  ASSERT_TRUE(sampler);
282 
283  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
284  SinglePassCallback callback = [&](RenderPass& pass) {
285  for (size_t i = 0; i < 1; i++) {
286  for (size_t j = 0; j < 1; j++) {
287  pass.SetCommandLabel("Box");
288  pass.SetPipeline(box_pipeline);
289  pass.SetVertexBuffer(vertex_buffer);
290 
291  FS::FrameInfo frame_info;
292  frame_info.current_time = GetSecondsElapsed();
293  frame_info.cursor_position = GetCursorPosition();
294  frame_info.window_size.x = GetWindowSize().width;
295  frame_info.window_size.y = GetWindowSize().height;
296 
297  FS::BindFrameInfo(pass, data_host_buffer->EmplaceUniform(frame_info));
298  FS::BindContents1(pass, boston, sampler);
299  FS::BindContents2(pass, bridge, sampler);
300 
301  VS::UniformBuffer uniforms;
302  EXPECT_EQ(pass.GetOrthographicTransform(),
303  Matrix::MakeOrthographic(pass.GetRenderTargetSize()));
304  uniforms.mvp = pass.GetOrthographicTransform() *
305  Matrix::MakeScale(GetContentScale()) *
306  Matrix::MakeTranslation({i * 50.0f, j * 50.0f, 0.0f});
307  VS::BindUniformBuffer(pass, data_host_buffer->EmplaceUniform(uniforms));
308  if (!pass.Draw().ok()) {
309  return false;
310  }
311  }
312  }
313 
314  data_host_buffer->Reset();
315  return true;
316  };
317  OpenPlaygroundHere(callback);
318 }
319 
320 TEST_P(RendererTest, CanRenderToTexture) {
321  using VS = BoxFadeVertexShader;
322  using FS = BoxFadeFragmentShader;
323  auto context = GetContext();
324  ASSERT_TRUE(context);
325  using BoxPipelineBuilder = PipelineBuilder<VS, FS>;
326  auto pipeline_desc =
327  BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context);
328  pipeline_desc->SetSampleCount(SampleCount::kCount1);
329  pipeline_desc->ClearDepthAttachment();
330  pipeline_desc->SetStencilPixelFormat(PixelFormat::kS8UInt);
331 
332  ASSERT_TRUE(pipeline_desc.has_value());
333  auto box_pipeline =
334  context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
335  ASSERT_TRUE(box_pipeline);
336  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
337 
339  vertex_builder.SetLabel("Box");
340  vertex_builder.AddVertices({
341  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
342  {{800, 100, 0.0}, {1.0, 0.0}}, // 2
343  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
344  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
345  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
346  {{100, 800, 0.0}, {0.0, 1.0}}, // 4
347  });
348  auto vertex_buffer =
349  vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
350  ASSERT_TRUE(vertex_buffer);
351 
352  auto bridge = CreateTextureForFixture("bay_bridge.jpg");
353  auto boston = CreateTextureForFixture("boston.jpg");
354  ASSERT_TRUE(bridge && boston);
355  raw_ptr<const Sampler> sampler = context->GetSamplerLibrary()->GetSampler({});
356  ASSERT_TRUE(sampler);
357 
358  std::shared_ptr<RenderPass> r2t_pass;
359  auto cmd_buffer = context->CreateCommandBuffer();
360  ASSERT_TRUE(cmd_buffer);
361  {
362  ColorAttachment color0;
365 
366  TextureDescriptor texture_descriptor;
367  ASSERT_NE(pipeline_desc->GetColorAttachmentDescriptor(0u), nullptr);
368  texture_descriptor.format =
369  pipeline_desc->GetColorAttachmentDescriptor(0u)->format;
370  texture_descriptor.storage_mode = StorageMode::kHostVisible;
371  texture_descriptor.size = {400, 400};
372  texture_descriptor.mip_count = 1u;
373  texture_descriptor.usage = TextureUsage::kRenderTarget;
374 
375  color0.texture =
376  context->GetResourceAllocator()->CreateTexture(texture_descriptor);
377 
378  ASSERT_TRUE(color0.IsValid());
379 
380  color0.texture->SetLabel("r2t_target");
381 
382  StencilAttachment stencil0;
383  stencil0.load_action = LoadAction::kClear;
385  TextureDescriptor stencil_texture_desc;
386  stencil_texture_desc.storage_mode = StorageMode::kDeviceTransient;
387  stencil_texture_desc.size = texture_descriptor.size;
388  stencil_texture_desc.format = PixelFormat::kS8UInt;
389  stencil_texture_desc.usage = TextureUsage::kRenderTarget;
390  stencil0.texture =
391  context->GetResourceAllocator()->CreateTexture(stencil_texture_desc);
392 
393  RenderTarget r2t_desc;
394  r2t_desc.SetColorAttachment(color0, 0u);
395  r2t_desc.SetStencilAttachment(stencil0);
396  r2t_pass = cmd_buffer->CreateRenderPass(r2t_desc);
397  ASSERT_TRUE(r2t_pass && r2t_pass->IsValid());
398  }
399 
400  r2t_pass->SetCommandLabel("Box");
401  r2t_pass->SetPipeline(box_pipeline);
402  r2t_pass->SetVertexBuffer(vertex_buffer);
403 
404  FS::FrameInfo frame_info;
405  frame_info.current_time = GetSecondsElapsed();
406  frame_info.cursor_position = GetCursorPosition();
407  frame_info.window_size.x = GetWindowSize().width;
408  frame_info.window_size.y = GetWindowSize().height;
409 
410  FS::BindFrameInfo(*r2t_pass, data_host_buffer->EmplaceUniform(frame_info));
411  FS::BindContents1(*r2t_pass, boston, sampler);
412  FS::BindContents2(*r2t_pass, bridge, sampler);
413 
414  VS::UniformBuffer uniforms;
415  uniforms.mvp = Matrix::MakeOrthographic(ISize{1024, 768}) *
416  Matrix::MakeTranslation({50.0f, 50.0f, 0.0f});
417  VS::BindUniformBuffer(*r2t_pass, data_host_buffer->EmplaceUniform(uniforms));
418  ASSERT_TRUE(r2t_pass->Draw().ok());
419  ASSERT_TRUE(r2t_pass->EncodeCommands());
420  ASSERT_TRUE(context->FlushCommandBuffers());
421 }
422 
423 TEST_P(RendererTest, CanRenderInstanced) {
424  if (GetParam() == PlaygroundBackend::kOpenGLES ||
425  GetParam() == PlaygroundBackend::kOpenGLESSDF) {
426  // This test drives instancing through gl_InstanceIndex and a storage
427  // buffer, both of which require OpenGL ES 3.1. The portable instance-rate
428  // vertex attribute path, which works down to OpenGL ES 2.0, is covered by
429  // CanRenderInstancedWithVertexAttributes.
430  GTEST_SKIP() << "This test's instance-ID mechanism requires OpenGL ES 3.1; "
431  "CanRenderInstancedWithVertexAttributes covers the "
432  "portable instance-rate path.";
433  }
434  using VS = InstancedDrawVertexShader;
435  using FS = InstancedDrawFragmentShader;
436 
438  builder.AddVertices({
439  VS::PerVertexData{Point{10, 10}},
440  VS::PerVertexData{Point{10, 110}},
441  VS::PerVertexData{Point{110, 10}},
442  VS::PerVertexData{Point{10, 110}},
443  VS::PerVertexData{Point{110, 10}},
444  VS::PerVertexData{Point{110, 110}},
445  });
446 
447  ASSERT_NE(GetContext(), nullptr);
448  auto pipeline =
449  GetContext()
450  ->GetPipelineLibrary()
452  *GetContext())
453  ->SetSampleCount(SampleCount::kCount4)
454  .SetStencilAttachmentDescriptors(std::nullopt))
455 
456  .Get();
457  ASSERT_TRUE(pipeline && pipeline->IsValid());
458 
459  static constexpr size_t kInstancesCount = 5u;
460  VS::InstanceInfo<kInstancesCount> instances;
461  for (size_t i = 0; i < kInstancesCount; i++) {
462  instances.colors[i] = Color::Random();
463  }
464 
465  auto [data_host_buffer, indexes_host_buffer] =
466  createHostBuffers(GetContext());
467  ASSERT_TRUE(OpenPlaygroundHere([&](RenderPass& pass) -> bool {
468  pass.SetPipeline(pipeline);
469  pass.SetCommandLabel("InstancedDraw");
470 
471  VS::FrameInfo frame_info;
472  EXPECT_EQ(pass.GetOrthographicTransform(),
474  frame_info.mvp =
475  pass.GetOrthographicTransform() * Matrix::MakeScale(GetContentScale());
476  VS::BindFrameInfo(pass, data_host_buffer->EmplaceUniform(frame_info));
477  VS::BindInstanceInfo(pass,
478  data_host_buffer->EmplaceStorageBuffer(instances));
479  pass.SetVertexBuffer(
480  builder.CreateVertexBuffer(*data_host_buffer, *indexes_host_buffer));
481 
482  pass.SetInstanceCount(kInstancesCount);
483  pass.Draw();
484 
485  data_host_buffer->Reset();
486  return true;
487  }));
488 }
489 
490 TEST_P(RendererTest, CanBlitTextureToTexture) {
491  if (GetBackend() == PlaygroundBackend::kOpenGLES ||
492  GetBackend() == PlaygroundBackend::kOpenGLESSDF) {
493  GTEST_SKIP() << "Mipmap test shader not supported on GLES.";
494  }
495  auto context = GetContext();
496  ASSERT_TRUE(context);
497 
498  using VS = MipmapsVertexShader;
499  using FS = MipmapsFragmentShader;
501  ASSERT_TRUE(desc.has_value());
502  desc->SetSampleCount(SampleCount::kCount4);
503  desc->SetStencilAttachmentDescriptors(std::nullopt);
504  auto mipmaps_pipeline =
505  context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
506  ASSERT_TRUE(mipmaps_pipeline);
507 
508  TextureDescriptor texture_desc;
510  texture_desc.format = PixelFormat::kR8G8B8A8UNormInt;
511  texture_desc.size = {800, 600};
512  texture_desc.mip_count = 1u;
514  auto texture = context->GetResourceAllocator()->CreateTexture(texture_desc);
515  ASSERT_TRUE(texture);
516 
517  auto bridge = CreateTextureForFixture("bay_bridge.jpg");
518  auto boston = CreateTextureForFixture("boston.jpg");
519  ASSERT_TRUE(bridge && boston);
520  raw_ptr<const Sampler> sampler = context->GetSamplerLibrary()->GetSampler({});
521  ASSERT_TRUE(sampler);
522 
523  // Vertex buffer.
525  vertex_builder.SetLabel("Box");
526  auto size = Point(boston->GetSize());
527  vertex_builder.AddVertices({
528  {{0, 0}, {0.0, 0.0}}, // 1
529  {{size.x, 0}, {1.0, 0.0}}, // 2
530  {{size.x, size.y}, {1.0, 1.0}}, // 3
531  {{0, 0}, {0.0, 0.0}}, // 1
532  {{size.x, size.y}, {1.0, 1.0}}, // 3
533  {{0, size.y}, {0.0, 1.0}}, // 4
534  });
535  auto vertex_buffer =
536  vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
537  ASSERT_TRUE(vertex_buffer);
538 
539  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
540  Playground::RenderCallback callback = [&](RenderTarget& render_target) {
541  auto buffer = context->CreateCommandBuffer();
542  if (!buffer) {
543  return false;
544  }
545  buffer->SetLabel("Playground Command Buffer");
546 
547  {
548  auto pass = buffer->CreateBlitPass();
549  if (!pass) {
550  return false;
551  }
552  pass->SetLabel("Playground Blit Pass");
553 
554  // Blit `bridge` to the top left corner of the texture.
555  pass->AddCopy(bridge, texture);
556 
557  if (!pass->EncodeCommands()) {
558  return false;
559  }
560  }
561 
562  {
563  auto pass = buffer->CreateRenderPass(render_target);
564  if (!pass) {
565  return false;
566  }
567  pass->SetLabel("Playground Render Pass");
568  {
569  pass->SetCommandLabel("Image");
570  pass->SetPipeline(mipmaps_pipeline);
571  pass->SetVertexBuffer(vertex_buffer);
572 
573  VS::FrameInfo frame_info;
574  EXPECT_EQ(pass->GetOrthographicTransform(),
575  Matrix::MakeOrthographic(pass->GetRenderTargetSize()));
576  frame_info.mvp = pass->GetOrthographicTransform() *
577  Matrix::MakeScale(GetContentScale());
578  VS::BindFrameInfo(*pass, data_host_buffer->EmplaceUniform(frame_info));
579 
580  FS::FragInfo frag_info;
581  frag_info.lod = 0;
582  FS::BindFragInfo(*pass, data_host_buffer->EmplaceUniform(frag_info));
583 
584  auto sampler = context->GetSamplerLibrary()->GetSampler({});
585  FS::BindTex(*pass, texture, sampler);
586 
587  pass->Draw();
588  }
589  pass->EncodeCommands();
590  }
591 
592  if (!context->GetCommandQueue()->Submit({buffer}).ok()) {
593  return false;
594  }
595  data_host_buffer->Reset();
596  return true;
597  };
598  OpenPlaygroundHere(callback);
599 }
600 
601 TEST_P(RendererTest, CanBlitTextureToBuffer) {
602  if (GetBackend() == PlaygroundBackend::kOpenGLES ||
603  GetBackend() == PlaygroundBackend::kOpenGLESSDF) {
604  GTEST_SKIP() << "Mipmap test shader not supported on GLES.";
605  }
606  auto context = GetContext();
607  ASSERT_TRUE(context);
608 
609  using VS = MipmapsVertexShader;
610  using FS = MipmapsFragmentShader;
612  ASSERT_TRUE(desc.has_value());
613  desc->SetSampleCount(SampleCount::kCount4);
614  desc->SetStencilAttachmentDescriptors(std::nullopt);
615  auto mipmaps_pipeline =
616  context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
617  ASSERT_TRUE(mipmaps_pipeline);
618 
619  auto bridge = CreateTextureForFixture("bay_bridge.jpg");
620  auto boston = CreateTextureForFixture("boston.jpg");
621  ASSERT_TRUE(bridge && boston);
622  raw_ptr<const Sampler> sampler = context->GetSamplerLibrary()->GetSampler({});
623  ASSERT_TRUE(sampler);
624 
625  TextureDescriptor texture_desc;
627  texture_desc.format = PixelFormat::kR8G8B8A8UNormInt;
628  texture_desc.size = bridge->GetTextureDescriptor().size;
629  texture_desc.mip_count = 1u;
630  texture_desc.usage = TextureUsage::kRenderTarget |
632  DeviceBufferDescriptor device_buffer_desc;
633  device_buffer_desc.storage_mode = StorageMode::kHostVisible;
634  device_buffer_desc.size =
635  bridge->GetTextureDescriptor().GetByteSizeOfBaseMipLevel();
636  auto device_buffer =
637  context->GetResourceAllocator()->CreateBuffer(device_buffer_desc);
638 
639  // Vertex buffer.
641  vertex_builder.SetLabel("Box");
642  auto size = Point(boston->GetSize());
643  vertex_builder.AddVertices({
644  {{0, 0}, {0.0, 0.0}}, // 1
645  {{size.x, 0}, {1.0, 0.0}}, // 2
646  {{size.x, size.y}, {1.0, 1.0}}, // 3
647  {{0, 0}, {0.0, 0.0}}, // 1
648  {{size.x, size.y}, {1.0, 1.0}}, // 3
649  {{0, size.y}, {0.0, 1.0}}, // 4
650  });
651  auto vertex_buffer =
652  vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
653  ASSERT_TRUE(vertex_buffer);
654 
655  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
656  Playground::RenderCallback callback = [&](RenderTarget& render_target) {
657  {
658  auto buffer = context->CreateCommandBuffer();
659  if (!buffer) {
660  return false;
661  }
662  buffer->SetLabel("Playground Command Buffer");
663  auto pass = buffer->CreateBlitPass();
664  if (!pass) {
665  return false;
666  }
667  pass->SetLabel("Playground Blit Pass");
668 
669  // Blit `bridge` to the top left corner of the texture.
670  pass->AddCopy(bridge, device_buffer);
671  pass->EncodeCommands();
672 
673  if (!context->GetCommandQueue()->Submit({buffer}).ok()) {
674  return false;
675  }
676  }
677 
678  {
679  auto buffer = context->CreateCommandBuffer();
680  if (!buffer) {
681  return false;
682  }
683  buffer->SetLabel("Playground Command Buffer");
684 
685  auto pass = buffer->CreateRenderPass(render_target);
686  if (!pass) {
687  return false;
688  }
689  pass->SetLabel("Playground Render Pass");
690  {
691  pass->SetCommandLabel("Image");
692  pass->SetPipeline(mipmaps_pipeline);
693  pass->SetVertexBuffer(vertex_buffer);
694 
695  VS::FrameInfo frame_info;
696  EXPECT_EQ(pass->GetOrthographicTransform(),
697  Matrix::MakeOrthographic(pass->GetRenderTargetSize()));
698  frame_info.mvp = pass->GetOrthographicTransform() *
699  Matrix::MakeScale(GetContentScale());
700  VS::BindFrameInfo(*pass, data_host_buffer->EmplaceUniform(frame_info));
701 
702  FS::FragInfo frag_info;
703  frag_info.lod = 0;
704  FS::BindFragInfo(*pass, data_host_buffer->EmplaceUniform(frag_info));
705 
706  raw_ptr<const Sampler> sampler =
707  context->GetSamplerLibrary()->GetSampler({});
708  auto buffer_view = DeviceBuffer::AsBufferView(device_buffer);
709  auto texture =
710  context->GetResourceAllocator()->CreateTexture(texture_desc);
711  if (!texture->SetContents(device_buffer->OnGetContents(),
712  buffer_view.GetRange().length)) {
713  VALIDATION_LOG << "Could not upload texture to device memory";
714  return false;
715  }
716  FS::BindTex(*pass, texture, sampler);
717 
718  pass->Draw().ok();
719  }
720  pass->EncodeCommands();
721  if (!context->GetCommandQueue()->Submit({buffer}).ok()) {
722  return false;
723  }
724  }
725  data_host_buffer->Reset();
726  return true;
727  };
728  OpenPlaygroundHere(callback);
729 }
730 
731 TEST_P(RendererTest, CanGenerateMipmaps) {
732  if (GetBackend() == PlaygroundBackend::kOpenGLES ||
733  GetBackend() == PlaygroundBackend::kOpenGLESSDF) {
734  GTEST_SKIP() << "Mipmap test shader not supported on GLES.";
735  }
736  auto context = GetContext();
737  ASSERT_TRUE(context);
738 
739  using VS = MipmapsVertexShader;
740  using FS = MipmapsFragmentShader;
742  ASSERT_TRUE(desc.has_value());
743  desc->SetSampleCount(SampleCount::kCount4);
744  desc->SetStencilAttachmentDescriptors(std::nullopt);
745  auto mipmaps_pipeline =
746  context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
747  ASSERT_TRUE(mipmaps_pipeline);
748 
749  auto boston = CreateTextureForFixture("boston.jpg", true);
750  ASSERT_TRUE(boston);
751 
752  // Vertex buffer.
754  vertex_builder.SetLabel("Box");
755  auto size = Point(boston->GetSize());
756  vertex_builder.AddVertices({
757  {{0, 0}, {0.0, 0.0}}, // 1
758  {{size.x, 0}, {1.0, 0.0}}, // 2
759  {{size.x, size.y}, {1.0, 1.0}}, // 3
760  {{0, 0}, {0.0, 0.0}}, // 1
761  {{size.x, size.y}, {1.0, 1.0}}, // 3
762  {{0, size.y}, {0.0, 1.0}}, // 4
763  });
764  auto vertex_buffer =
765  vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
766  ASSERT_TRUE(vertex_buffer);
767 
768  bool first_frame = true;
769  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
770  Playground::RenderCallback callback = [&](RenderTarget& render_target) {
771  const char* mip_filter_names[] = {"Base", "Nearest", "Linear"};
772  const MipFilter mip_filters[] = {MipFilter::kBase, MipFilter::kNearest,
774  const char* min_filter_names[] = {"Nearest", "Linear"};
775  const MinMagFilter min_filters[] = {MinMagFilter::kNearest,
777 
778  // UI state.
779  static int selected_mip_filter = 1;
780  static int selected_min_filter = 0;
781  static float lod = 4.5;
782 
783  ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
784  ImGui::Combo("Mip filter", &selected_mip_filter, mip_filter_names,
785  sizeof(mip_filter_names) / sizeof(char*));
786  ImGui::Combo("Min filter", &selected_min_filter, min_filter_names,
787  sizeof(min_filter_names) / sizeof(char*));
788  ImGui::SliderFloat("LOD", &lod, 0, boston->GetMipCount() - 1);
789  ImGui::End();
790 
791  auto buffer = context->CreateCommandBuffer();
792  if (!buffer) {
793  return false;
794  }
795  buffer->SetLabel("Playground Command Buffer");
796 
797  if (first_frame) {
798  auto pass = buffer->CreateBlitPass();
799  if (!pass) {
800  return false;
801  }
802  pass->SetLabel("Playground Blit Pass");
803 
804  pass->GenerateMipmap(boston, "Boston Mipmap");
805 
806  pass->EncodeCommands();
807  }
808 
809  first_frame = false;
810 
811  {
812  auto pass = buffer->CreateRenderPass(render_target);
813  if (!pass) {
814  return false;
815  }
816  pass->SetLabel("Playground Render Pass");
817  {
818  pass->SetCommandLabel("Image LOD");
819  pass->SetPipeline(mipmaps_pipeline);
820  pass->SetVertexBuffer(vertex_buffer);
821 
822  VS::FrameInfo frame_info;
823  EXPECT_EQ(pass->GetOrthographicTransform(),
824  Matrix::MakeOrthographic(pass->GetRenderTargetSize()));
825  frame_info.mvp = pass->GetOrthographicTransform() *
826  Matrix::MakeScale(GetContentScale());
827  VS::BindFrameInfo(*pass, data_host_buffer->EmplaceUniform(frame_info));
828 
829  FS::FragInfo frag_info;
830  frag_info.lod = lod;
831  FS::BindFragInfo(*pass, data_host_buffer->EmplaceUniform(frag_info));
832 
833  SamplerDescriptor sampler_desc;
834  sampler_desc.mip_filter = mip_filters[selected_mip_filter];
835  sampler_desc.min_filter = min_filters[selected_min_filter];
836  raw_ptr<const Sampler> sampler =
837  context->GetSamplerLibrary()->GetSampler(sampler_desc);
838  FS::BindTex(*pass, boston, sampler);
839 
840  pass->Draw();
841  }
842  pass->EncodeCommands();
843  }
844 
845  if (!context->GetCommandQueue()->Submit({buffer}).ok()) {
846  return false;
847  }
848  data_host_buffer->Reset();
849  return true;
850  };
851  OpenPlaygroundHere(callback);
852 }
853 
854 TEST_P(RendererTest, TheImpeller) {
855  using VS = ImpellerVertexShader;
856  using FS = ImpellerFragmentShader;
857 
858  auto context = GetContext();
859  auto pipeline_descriptor =
861  ASSERT_TRUE(pipeline_descriptor.has_value());
862  pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
863  pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
864  auto pipeline =
865  context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
866  ASSERT_TRUE(pipeline && pipeline->IsValid());
867 
868  auto blue_noise = CreateTextureForFixture("blue_noise.png");
869  SamplerDescriptor noise_sampler_desc;
870  noise_sampler_desc.width_address_mode = SamplerAddressMode::kRepeat;
872  raw_ptr<const Sampler> noise_sampler =
873  context->GetSamplerLibrary()->GetSampler(noise_sampler_desc);
874 
875  auto cube_map = CreateTextureCubeForFixture(
876  {"table_mountain_px.png", "table_mountain_nx.png",
877  "table_mountain_py.png", "table_mountain_ny.png",
878  "table_mountain_pz.png", "table_mountain_nz.png"});
879  raw_ptr<const Sampler> cube_map_sampler =
880  context->GetSamplerLibrary()->GetSampler({});
881 
882  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
883  SinglePassCallback callback = [&](RenderPass& pass) {
884  auto size = pass.GetRenderTargetSize();
885 
886  pass.SetPipeline(pipeline);
887  pass.SetCommandLabel("Impeller SDF scene");
889  builder.AddVertices({{Point()},
890  {Point(0, size.height)},
891  {Point(size.width, 0)},
892  {Point(size.width, 0)},
893  {Point(0, size.height)},
894  {Point(size.width, size.height)}});
895  pass.SetVertexBuffer(
896  builder.CreateVertexBuffer(*data_host_buffer, *indexes_host_buffer));
897 
898  VS::FrameInfo frame_info;
899  EXPECT_EQ(pass.GetOrthographicTransform(), Matrix::MakeOrthographic(size));
900  frame_info.mvp = pass.GetOrthographicTransform();
901  VS::BindFrameInfo(pass, data_host_buffer->EmplaceUniform(frame_info));
902 
903  FS::FragInfo fs_uniform;
904  fs_uniform.texture_size = Point(size);
905  fs_uniform.time = GetSecondsElapsed();
906  FS::BindFragInfo(pass, data_host_buffer->EmplaceUniform(fs_uniform));
907  FS::BindBlueNoise(pass, blue_noise, noise_sampler);
908  FS::BindCubeMap(pass, cube_map, cube_map_sampler);
909 
910  pass.Draw().ok();
911  data_host_buffer->Reset();
912  return true;
913  };
914  OpenPlaygroundHere(callback);
915 }
916 
918  using VS = PlanetVertexShader;
919  using FS = PlanetFragmentShader;
920 
921  auto context = GetContext();
922  auto pipeline_descriptor =
924  ASSERT_TRUE(pipeline_descriptor.has_value());
925  pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
926  pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
927  auto pipeline =
928  context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
929  ASSERT_TRUE(pipeline && pipeline->IsValid());
930 
931  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
932  SinglePassCallback callback = [&](RenderPass& pass) {
933  static Scalar speed = 0.1;
934  static Scalar planet_size = 550.0;
935  static bool show_normals = false;
936  static bool show_noise = false;
937  static Scalar seed_value = 42.0;
938 
939  auto size = pass.GetRenderTargetSize();
940 
941  ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
942  ImGui::SliderFloat("Speed", &speed, 0.0, 10.0);
943  ImGui::SliderFloat("Planet Size", &planet_size, 0.1, 1000);
944  ImGui::Checkbox("Show Normals", &show_normals);
945  ImGui::Checkbox("Show Noise", &show_noise);
946  ImGui::InputFloat("Seed Value", &seed_value);
947  ImGui::End();
948 
949  pass.SetPipeline(pipeline);
950  pass.SetCommandLabel("Planet scene");
952  builder.AddVertices({{Point()},
953  {Point(0, size.height)},
954  {Point(size.width, 0)},
955  {Point(size.width, 0)},
956  {Point(0, size.height)},
957  {Point(size.width, size.height)}});
958  pass.SetVertexBuffer(
959  builder.CreateVertexBuffer(*data_host_buffer, *indexes_host_buffer));
960 
961  VS::FrameInfo frame_info;
962  EXPECT_EQ(pass.GetOrthographicTransform(), Matrix::MakeOrthographic(size));
963  frame_info.mvp = pass.GetOrthographicTransform();
964  VS::BindFrameInfo(pass, data_host_buffer->EmplaceUniform(frame_info));
965 
966  FS::FragInfo fs_uniform;
967  fs_uniform.resolution = Point(size);
968  fs_uniform.time = GetSecondsElapsed();
969  fs_uniform.speed = speed;
970  fs_uniform.planet_size = planet_size;
971  fs_uniform.show_normals = show_normals ? 1.0 : 0.0;
972  fs_uniform.show_noise = show_noise ? 1.0 : 0.0;
973  fs_uniform.seed_value = seed_value;
974  FS::BindFragInfo(pass, data_host_buffer->EmplaceUniform(fs_uniform));
975 
976  pass.Draw().ok();
977  data_host_buffer->Reset();
978  return true;
979  };
980  OpenPlaygroundHere(callback);
981 }
982 
983 TEST_P(RendererTest, ArrayUniforms) {
984  using VS = ArrayVertexShader;
985  using FS = ArrayFragmentShader;
986 
987  auto context = GetContext();
988  auto pipeline_descriptor =
990  ASSERT_TRUE(pipeline_descriptor.has_value());
991  pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
992  pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
993  auto pipeline =
994  context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
995  ASSERT_TRUE(pipeline && pipeline->IsValid());
996 
997  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
998  SinglePassCallback callback = [&](RenderPass& pass) {
999  auto size = pass.GetRenderTargetSize();
1000 
1001  pass.SetPipeline(pipeline);
1002  pass.SetCommandLabel("Google Dots");
1004  builder.AddVertices({{Point()},
1005  {Point(0, size.height)},
1006  {Point(size.width, 0)},
1007  {Point(size.width, 0)},
1008  {Point(0, size.height)},
1009  {Point(size.width, size.height)}});
1010  pass.SetVertexBuffer(
1011  builder.CreateVertexBuffer(*data_host_buffer, *indexes_host_buffer));
1012 
1013  VS::FrameInfo frame_info;
1014  EXPECT_EQ(pass.GetOrthographicTransform(), Matrix::MakeOrthographic(size));
1015  frame_info.mvp =
1016  pass.GetOrthographicTransform() * Matrix::MakeScale(GetContentScale());
1017  VS::BindFrameInfo(pass, data_host_buffer->EmplaceUniform(frame_info));
1018 
1019  auto time = GetSecondsElapsed();
1020  auto y_pos = [&time](float x) {
1021  return 400 + 10 * std::cos(time * 5 + x / 6);
1022  };
1023 
1024  FS::FragInfo fs_uniform = {
1025  .circle_positions = {Point(430, y_pos(0)), Point(480, y_pos(1)),
1026  Point(530, y_pos(2)), Point(580, y_pos(3))},
1027  .colors = {Color::MakeRGBA8(66, 133, 244, 255),
1028  Color::MakeRGBA8(219, 68, 55, 255),
1029  Color::MakeRGBA8(244, 180, 0, 255),
1030  Color::MakeRGBA8(15, 157, 88, 255)},
1031  };
1032  FS::BindFragInfo(pass, data_host_buffer->EmplaceUniform(fs_uniform));
1033 
1034  pass.Draw();
1035  data_host_buffer->Reset();
1036  return true;
1037  };
1038  OpenPlaygroundHere(callback);
1039 }
1040 
1041 TEST_P(RendererTest, InactiveUniforms) {
1042  using VS = InactiveUniformsVertexShader;
1043  using FS = InactiveUniformsFragmentShader;
1044 
1045  auto context = GetContext();
1046  auto pipeline_descriptor =
1048  ASSERT_TRUE(pipeline_descriptor.has_value());
1049  pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
1050  pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
1051  auto pipeline =
1052  context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
1053  ASSERT_TRUE(pipeline && pipeline->IsValid());
1054 
1055  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
1056  SinglePassCallback callback = [&](RenderPass& pass) {
1057  auto size = pass.GetRenderTargetSize();
1058 
1059  pass.SetPipeline(pipeline);
1060  pass.SetCommandLabel("Inactive Uniform");
1061 
1063  builder.AddVertices({{Point()},
1064  {Point(0, size.height)},
1065  {Point(size.width, 0)},
1066  {Point(size.width, 0)},
1067  {Point(0, size.height)},
1068  {Point(size.width, size.height)}});
1069  pass.SetVertexBuffer(
1070  builder.CreateVertexBuffer(*data_host_buffer, *indexes_host_buffer));
1071 
1072  VS::FrameInfo frame_info;
1073  EXPECT_EQ(pass.GetOrthographicTransform(), Matrix::MakeOrthographic(size));
1074  frame_info.mvp =
1075  pass.GetOrthographicTransform() * Matrix::MakeScale(GetContentScale());
1076  VS::BindFrameInfo(pass, data_host_buffer->EmplaceUniform(frame_info));
1077 
1078  FS::FragInfo fs_uniform = {.unused_color = Color::Red(),
1079  .color = Color::Green()};
1080  FS::BindFragInfo(pass, data_host_buffer->EmplaceUniform(fs_uniform));
1081 
1082  pass.Draw().ok();
1083  data_host_buffer->Reset();
1084  return true;
1085  };
1086  OpenPlaygroundHere(callback);
1087 }
1088 
1089 TEST_P(RendererTest, DefaultIndexSize) {
1090  using VS = BoxFadeVertexShader;
1091 
1092  // Default to 16bit index buffer size, as this is a reasonable default and
1093  // supported on all backends without extensions.
1095  vertex_builder.AppendIndex(0u);
1096  ASSERT_EQ(vertex_builder.GetIndexType(), IndexType::k16bit);
1097 }
1098 
1099 TEST_P(RendererTest, DefaultIndexBehavior) {
1100  using VS = BoxFadeVertexShader;
1101 
1102  // Do not create any index buffer if no indices were provided.
1104  ASSERT_EQ(vertex_builder.GetIndexType(), IndexType::kNone);
1105 }
1106 
1108  // Does not create index buffer if one is provided.
1109  using VS = BoxFadeVertexShader;
1111  vertex_builder.SetLabel("Box");
1112  vertex_builder.AddVertices({
1113  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1114  {{800, 100, 0.0}, {1.0, 0.0}}, // 2
1115  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1116  {{100, 800, 0.0}, {0.0, 1.0}}, // 4
1117  });
1118  vertex_builder.AppendIndex(0);
1119  vertex_builder.AppendIndex(1);
1120  vertex_builder.AppendIndex(2);
1121  vertex_builder.AppendIndex(1);
1122  vertex_builder.AppendIndex(2);
1123  vertex_builder.AppendIndex(3);
1124 
1125  ASSERT_EQ(vertex_builder.GetIndexCount(), 6u);
1126  ASSERT_EQ(vertex_builder.GetVertexCount(), 4u);
1127 }
1128 
1130  public:
1132  labels_.push_back("Never");
1133  functions_.push_back(CompareFunction::kNever);
1134  labels_.push_back("Always");
1135  functions_.push_back(CompareFunction::kAlways);
1136  labels_.push_back("Less");
1137  functions_.push_back(CompareFunction::kLess);
1138  labels_.push_back("Equal");
1139  functions_.push_back(CompareFunction::kEqual);
1140  labels_.push_back("LessEqual");
1141  functions_.push_back(CompareFunction::kLessEqual);
1142  labels_.push_back("Greater");
1143  functions_.push_back(CompareFunction::kGreater);
1144  labels_.push_back("NotEqual");
1145  functions_.push_back(CompareFunction::kNotEqual);
1146  labels_.push_back("GreaterEqual");
1147  functions_.push_back(CompareFunction::kGreaterEqual);
1148  assert(labels_.size() == functions_.size());
1149  }
1150 
1151  const char* const* labels() const { return &labels_[0]; }
1152 
1153  int size() const { return labels_.size(); }
1154 
1155  int IndexOf(CompareFunction func) const {
1156  for (size_t i = 0; i < functions_.size(); i++) {
1157  if (functions_[i] == func) {
1158  return i;
1159  }
1160  }
1161  FML_UNREACHABLE();
1162  return -1;
1163  }
1164 
1165  CompareFunction FunctionOf(int index) const { return functions_[index]; }
1166 
1167  private:
1168  std::vector<const char*> labels_;
1169  std::vector<CompareFunction> functions_;
1170 };
1171 
1173  static CompareFunctionUIData data;
1174  return data;
1175 }
1176 
1177 TEST_P(RendererTest, StencilMask) {
1178  using VS = BoxFadeVertexShader;
1179  using FS = BoxFadeFragmentShader;
1180  auto context = GetContext();
1181  ASSERT_TRUE(context);
1182  using BoxFadePipelineBuilder = PipelineBuilder<VS, FS>;
1183  auto desc = BoxFadePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
1184  ASSERT_TRUE(desc.has_value());
1185 
1186  // Vertex buffer.
1188  vertex_builder.SetLabel("Box");
1189  vertex_builder.AddVertices({
1190  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1191  {{800, 100, 0.0}, {1.0, 0.0}}, // 2
1192  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1193  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1194  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1195  {{100, 800, 0.0}, {0.0, 1.0}}, // 4
1196  });
1197  auto vertex_buffer =
1198  vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
1199  ASSERT_TRUE(vertex_buffer);
1200 
1201  desc->SetSampleCount(SampleCount::kCount4);
1202  desc->SetStencilAttachmentDescriptors(std::nullopt);
1203 
1204  auto bridge = CreateTextureForFixture("bay_bridge.jpg");
1205  auto boston = CreateTextureForFixture("boston.jpg");
1206  ASSERT_TRUE(bridge && boston);
1207  raw_ptr<const Sampler> sampler = context->GetSamplerLibrary()->GetSampler({});
1208  ASSERT_TRUE(sampler);
1209 
1210  static bool mirror = false;
1211  static int stencil_reference_write = 0xFF;
1212  static int stencil_reference_read = 0x1;
1213  std::vector<uint8_t> stencil_contents;
1214  static int last_stencil_contents_reference_value = 0;
1215  static int current_front_compare =
1217  static int current_back_compare =
1219 
1220  auto [data_host_buffer, indexes_host_buffer] = createHostBuffers(context);
1221  Playground::RenderCallback callback = [&](RenderTarget& render_target) {
1222  auto buffer = context->CreateCommandBuffer();
1223  if (!buffer) {
1224  return false;
1225  }
1226  buffer->SetLabel("Playground Command Buffer");
1227 
1228  {
1229  // Configure the stencil attachment for the test.
1230  RenderTarget::AttachmentConfig stencil_config;
1231  stencil_config.load_action = LoadAction::kLoad;
1232  stencil_config.store_action = StoreAction::kDontCare;
1233  stencil_config.storage_mode = StorageMode::kHostVisible;
1234  render_target.SetupDepthStencilAttachments(
1235  *context, *context->GetResourceAllocator(),
1236  render_target.GetRenderTargetSize(), true, "stencil", stencil_config);
1237  // Fill the stencil buffer with an checkerboard pattern.
1238  const auto target_width = render_target.GetRenderTargetSize().width;
1239  const auto target_height = render_target.GetRenderTargetSize().height;
1240  const size_t target_size = target_width * target_height;
1241  if (stencil_contents.size() != target_size ||
1242  last_stencil_contents_reference_value != stencil_reference_write) {
1243  stencil_contents.resize(target_size);
1244  last_stencil_contents_reference_value = stencil_reference_write;
1245  for (int y = 0; y < target_height; y++) {
1246  for (int x = 0; x < target_width; x++) {
1247  const auto index = y * target_width + x;
1248  const auto kCheckSize = 64;
1249  const auto value =
1250  (((y / kCheckSize) + (x / kCheckSize)) % 2 == 0) *
1251  stencil_reference_write;
1252  stencil_contents[index] = value;
1253  }
1254  }
1255  }
1256  if (!render_target.GetStencilAttachment()->texture->SetContents(
1257  stencil_contents.data(), stencil_contents.size(), 0, false)) {
1258  VALIDATION_LOG << "Could not upload stencil contents to device memory";
1259  return false;
1260  }
1261  auto pass = buffer->CreateRenderPass(render_target);
1262  if (!pass) {
1263  return false;
1264  }
1265  pass->SetLabel("Stencil Buffer");
1266  ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
1267  ImGui::SliderInt("Stencil Write Value", &stencil_reference_write, 0,
1268  0xFF);
1269  ImGui::SliderInt("Stencil Compare Value", &stencil_reference_read, 0,
1270  0xFF);
1271  ImGui::Checkbox("Back face mode", &mirror);
1272  ImGui::ListBox("Front face compare function", &current_front_compare,
1273  CompareFunctionUI().labels(), CompareFunctionUI().size());
1274  ImGui::ListBox("Back face compare function", &current_back_compare,
1275  CompareFunctionUI().labels(), CompareFunctionUI().size());
1276  ImGui::End();
1277 
1279  front.stencil_compare =
1280  CompareFunctionUI().FunctionOf(current_front_compare);
1282  back.stencil_compare =
1283  CompareFunctionUI().FunctionOf(current_back_compare);
1284  desc->SetStencilAttachmentDescriptors(front, back);
1285  auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get();
1286 
1287  assert(pipeline && pipeline->IsValid());
1288 
1289  pass->SetCommandLabel("Box");
1290  pass->SetPipeline(pipeline);
1291  pass->SetStencilReference(stencil_reference_read);
1292  pass->SetVertexBuffer(vertex_buffer);
1293 
1294  VS::UniformBuffer uniforms;
1295  EXPECT_EQ(pass->GetOrthographicTransform(),
1296  Matrix::MakeOrthographic(pass->GetRenderTargetSize()));
1297  uniforms.mvp = pass->GetOrthographicTransform() *
1298  Matrix::MakeScale(GetContentScale());
1299  if (mirror) {
1300  uniforms.mvp = Matrix::MakeScale(Vector2(-1, 1)) * uniforms.mvp;
1301  }
1302  VS::BindUniformBuffer(*pass, data_host_buffer->EmplaceUniform(uniforms));
1303 
1304  FS::FrameInfo frame_info;
1305  frame_info.current_time = GetSecondsElapsed();
1306  frame_info.cursor_position = GetCursorPosition();
1307  frame_info.window_size.x = GetWindowSize().width;
1308  frame_info.window_size.y = GetWindowSize().height;
1309 
1310  FS::BindFrameInfo(*pass, data_host_buffer->EmplaceUniform(frame_info));
1311  FS::BindContents1(*pass, boston, sampler);
1312  FS::BindContents2(*pass, bridge, sampler);
1313  if (!pass->Draw().ok()) {
1314  return false;
1315  }
1316  pass->EncodeCommands();
1317  }
1318 
1319  if (!context->GetCommandQueue()->Submit({buffer}).ok()) {
1320  return false;
1321  }
1322  data_host_buffer->Reset();
1323  return true;
1324  };
1325  OpenPlaygroundHere(callback);
1326 }
1327 
1328 TEST_P(RendererTest, CanLookupRenderTargetProperties) {
1329  auto context = GetContext();
1330  auto cmd_buffer = context->CreateCommandBuffer();
1331  auto render_target_cache = std::make_shared<RenderTargetAllocator>(
1332  GetContext()->GetResourceAllocator());
1333 
1334  auto render_target = render_target_cache->CreateOffscreen(
1335  *context, {100, 100}, /*mip_count=*/1);
1336  auto render_pass = cmd_buffer->CreateRenderPass(render_target);
1337 
1338  EXPECT_EQ(render_pass->GetSampleCount(), render_target.GetSampleCount());
1339  EXPECT_EQ(render_pass->GetRenderTargetPixelFormat(),
1340  render_target.GetRenderTargetPixelFormat());
1341  EXPECT_EQ(render_pass->HasStencilAttachment(),
1342  render_target.GetStencilAttachment().has_value());
1343  EXPECT_EQ(render_pass->GetRenderTargetSize(),
1344  render_target.GetRenderTargetSize());
1345  render_pass->EncodeCommands();
1346 }
1347 
1349  RenderTargetCreateOffscreenMSAASetsDefaultDepthStencilFormat) {
1350  auto context = GetContext();
1351  auto render_target_cache = std::make_shared<RenderTargetAllocator>(
1352  GetContext()->GetResourceAllocator());
1353 
1354  RenderTarget render_target = render_target_cache->CreateOffscreenMSAA(
1355  *context, {100, 100}, /*mip_count=*/1);
1356  EXPECT_EQ(render_target.GetDepthAttachment()
1357  ->texture->GetTextureDescriptor()
1358  .format,
1359  GetContext()->GetCapabilities()->GetDefaultDepthStencilFormat());
1360 }
1361 
1362 template <class VertexShader, class FragmentShader>
1363 std::shared_ptr<Pipeline<PipelineDescriptor>> CreateDefaultPipeline(
1364  const std::shared_ptr<Context>& context) {
1365  using TexturePipelineBuilder = PipelineBuilder<VertexShader, FragmentShader>;
1366  auto pipeline_desc =
1367  TexturePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
1368  if (!pipeline_desc.has_value()) {
1369  return nullptr;
1370  }
1371  pipeline_desc->SetSampleCount(SampleCount::kCount4);
1372  pipeline_desc->SetStencilAttachmentDescriptors(std::nullopt);
1373  auto pipeline =
1374  context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
1375  if (!pipeline || !pipeline->IsValid()) {
1376  return nullptr;
1377  }
1378  return pipeline;
1379 }
1380 
1381 TEST_P(RendererTest, CanSepiaToneWithSubpasses) {
1382  // Define shader types
1383  using TextureVS = TextureVertexShader;
1384  using TextureFS = TextureFragmentShader;
1385 
1386  using SepiaVS = SepiaVertexShader;
1387  using SepiaFS = SepiaFragmentShader;
1388 
1389  auto context = GetContext();
1390  ASSERT_TRUE(context);
1391 
1392  if (!context->GetCapabilities()->SupportsFramebufferFetch()) {
1393  GTEST_SKIP() << "This test uses framebuffer fetch and the backend doesn't "
1394  "support it.";
1395  return;
1396  }
1397 
1398  // Create pipelines.
1399  auto texture_pipeline = CreateDefaultPipeline<TextureVS, TextureFS>(context);
1400  auto sepia_pipeline = CreateDefaultPipeline<SepiaVS, SepiaFS>(context);
1401 
1402  ASSERT_TRUE(texture_pipeline);
1403  ASSERT_TRUE(sepia_pipeline);
1404 
1405  // Vertex buffer builders.
1407  texture_vtx_builder.AddVertices({
1408  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1409  {{800, 100, 0.0}, {1.0, 0.0}}, // 2
1410  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1411  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1412  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1413  {{100, 800, 0.0}, {0.0, 1.0}}, // 4
1414  });
1415 
1417  sepia_vtx_builder.AddVertices({
1418  {{100, 100, 0.0}}, // 1
1419  {{800, 100, 0.0}}, // 2
1420  {{800, 800, 0.0}}, // 3
1421  {{100, 100, 0.0}}, // 1
1422  {{800, 800, 0.0}}, // 3
1423  {{100, 800, 0.0}}, // 4
1424  });
1425 
1426  auto boston = CreateTextureForFixture("boston.jpg");
1427  ASSERT_TRUE(boston);
1428 
1429  const auto& sampler = context->GetSamplerLibrary()->GetSampler({});
1430  ASSERT_TRUE(sampler);
1431 
1432  auto buffer = HostBuffer::Create(
1433  context->GetResourceAllocator(), context->GetIdleWaiter(),
1434  context->GetCapabilities()->GetMinimumUniformAlignment());
1435  SinglePassCallback callback = [&](RenderPass& pass) {
1436  // Draw the texture.
1437  {
1438  pass.SetPipeline(texture_pipeline);
1439  pass.SetVertexBuffer(texture_vtx_builder.CreateVertexBuffer(
1440  *context->GetResourceAllocator()));
1441  TextureVS::UniformBuffer uniforms;
1442  uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
1443  Matrix::MakeScale(GetContentScale());
1444  TextureVS::BindUniformBuffer(pass, buffer->EmplaceUniform(uniforms));
1445  TextureFS::BindTextureContents(pass, boston, sampler);
1446  if (!pass.Draw().ok()) {
1447  return false;
1448  }
1449  }
1450 
1451  // Draw the sepia toner.
1452  {
1453  pass.SetPipeline(sepia_pipeline);
1454  pass.SetVertexBuffer(sepia_vtx_builder.CreateVertexBuffer(
1455  *context->GetResourceAllocator()));
1456  SepiaVS::UniformBuffer uniforms;
1457  uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
1458  Matrix::MakeScale(GetContentScale());
1459  SepiaVS::BindUniformBuffer(pass, buffer->EmplaceUniform(uniforms));
1460  if (!pass.Draw().ok()) {
1461  return false;
1462  }
1463  }
1464 
1465  return true;
1466  };
1467  OpenPlaygroundHere(callback);
1468 }
1469 
1470 TEST_P(RendererTest, CanSepiaToneThenSwizzleWithSubpasses) {
1471  // Define shader types
1472  using TextureVS = TextureVertexShader;
1473  using TextureFS = TextureFragmentShader;
1474 
1475  using SwizzleVS = SepiaVertexShader;
1476  using SwizzleFS = SwizzleFragmentShader;
1477 
1478  using SepiaVS = SepiaVertexShader;
1479  using SepiaFS = SepiaFragmentShader;
1480 
1481  auto context = GetContext();
1482  ASSERT_TRUE(context);
1483 
1484  if (!context->GetCapabilities()->SupportsFramebufferFetch()) {
1485  GTEST_SKIP() << "This test uses framebuffer fetch and the backend doesn't "
1486  "support it.";
1487  return;
1488  }
1489 
1490  // Create pipelines.
1491  auto texture_pipeline = CreateDefaultPipeline<TextureVS, TextureFS>(context);
1492  auto swizzle_pipeline = CreateDefaultPipeline<SwizzleVS, SwizzleFS>(context);
1493  auto sepia_pipeline = CreateDefaultPipeline<SepiaVS, SepiaFS>(context);
1494 
1495  ASSERT_TRUE(texture_pipeline);
1496  ASSERT_TRUE(swizzle_pipeline);
1497  ASSERT_TRUE(sepia_pipeline);
1498 
1499  // Vertex buffer builders.
1501  texture_vtx_builder.AddVertices({
1502  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1503  {{800, 100, 0.0}, {1.0, 0.0}}, // 2
1504  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1505  {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1506  {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1507  {{100, 800, 0.0}, {0.0, 1.0}}, // 4
1508  });
1509 
1511  sepia_vtx_builder.AddVertices({
1512  {{100, 100, 0.0}}, // 1
1513  {{800, 100, 0.0}}, // 2
1514  {{800, 800, 0.0}}, // 3
1515  {{100, 100, 0.0}}, // 1
1516  {{800, 800, 0.0}}, // 3
1517  {{100, 800, 0.0}}, // 4
1518  });
1519 
1520  auto boston = CreateTextureForFixture("boston.jpg");
1521  ASSERT_TRUE(boston);
1522 
1523  const auto& sampler = context->GetSamplerLibrary()->GetSampler({});
1524  ASSERT_TRUE(sampler);
1525 
1526  auto data_buffer = HostBuffer::Create(
1527  context->GetResourceAllocator(), context->GetIdleWaiter(),
1528  context->GetCapabilities()->GetMinimumUniformAlignment());
1529  SinglePassCallback callback = [&](RenderPass& pass) {
1530  // Draw the texture.
1531  {
1532  pass.SetPipeline(texture_pipeline);
1533  pass.SetVertexBuffer(texture_vtx_builder.CreateVertexBuffer(
1534  *context->GetResourceAllocator()));
1535  TextureVS::UniformBuffer uniforms;
1536  uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
1537  Matrix::MakeScale(GetContentScale());
1538  TextureVS::BindUniformBuffer(pass, data_buffer->EmplaceUniform(uniforms));
1539  TextureFS::BindTextureContents(pass, boston, sampler);
1540  if (!pass.Draw().ok()) {
1541  return false;
1542  }
1543  }
1544 
1545  // Draw the sepia toner.
1546  {
1547  pass.SetPipeline(sepia_pipeline);
1548  pass.SetVertexBuffer(sepia_vtx_builder.CreateVertexBuffer(
1549  *context->GetResourceAllocator()));
1550  SepiaVS::UniformBuffer uniforms;
1551  uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
1552  Matrix::MakeScale(GetContentScale());
1553  SepiaVS::BindUniformBuffer(pass, data_buffer->EmplaceUniform(uniforms));
1554  if (!pass.Draw().ok()) {
1555  return false;
1556  }
1557  }
1558 
1559  // Draw the swizzle.
1560  {
1561  pass.SetPipeline(swizzle_pipeline);
1562  pass.SetVertexBuffer(sepia_vtx_builder.CreateVertexBuffer(
1563  *context->GetResourceAllocator()));
1564  SwizzleVS::UniformBuffer uniforms;
1565  uniforms.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()) *
1566  Matrix::MakeScale(GetContentScale());
1567  SwizzleVS::BindUniformBuffer(pass, data_buffer->EmplaceUniform(uniforms));
1568  if (!pass.Draw().ok()) {
1569  return false;
1570  }
1571  }
1572 
1573  return true;
1574  };
1575  OpenPlaygroundHere(callback);
1576 }
1577 
1578 TEST_P(RendererTest, BindingNullTexturesDoesNotCrash) {
1579  using FS = BoxFadeFragmentShader;
1580 
1581  auto context = GetContext();
1582  raw_ptr<const Sampler> sampler = context->GetSamplerLibrary()->GetSampler({});
1583  auto command_buffer = context->CreateCommandBuffer();
1584 
1585  RenderTargetAllocator allocator(context->GetResourceAllocator());
1586  RenderTarget target = allocator.CreateOffscreen(*context, {1, 1}, 1);
1587 
1588  auto pass = command_buffer->CreateRenderPass(target);
1589  EXPECT_FALSE(FS::BindContents2(*pass, nullptr, sampler));
1590 }
1591 
1592 // Clears a single cube map face by attaching it as a render target slice.
1593 // Rendering to cube faces is portable down to OpenGL ES 2.0, so this runs on
1594 // every backend.
1595 TEST_P(RendererTest, CanRenderToTextureSlice) {
1596  auto context = GetContext();
1597  ASSERT_TRUE(context);
1598 
1599  TextureDescriptor desc;
1603  desc.size = {100, 100};
1605  auto texture = context->GetResourceAllocator()->CreateTexture(desc);
1606  ASSERT_TRUE(texture);
1607 
1608  ColorAttachment color0;
1609  color0.texture = texture;
1610  color0.slice = 3u; // +Y face.
1613  color0.clear_color = Color::Green();
1614  RenderTarget target;
1615  target.SetColorAttachment(color0, 0u);
1616 
1617  auto buffer = context->CreateCommandBuffer();
1618  auto pass = buffer->CreateRenderPass(target);
1619  ASSERT_TRUE(pass && pass->IsValid());
1620  pass->EncodeCommands();
1621  EXPECT_TRUE(context->GetCommandQueue()->Submit({buffer}).ok());
1622 }
1623 
1624 // Clears mip level 1 of a texture by attaching it as a render target. Skipped
1625 // on OpenGL ES, where rendering to non-zero mip levels needs ES 3.0 or
1626 // GL_OES_fbo_render_mipmap.
1627 TEST_P(RendererTest, CanRenderToMipLevel) {
1628  if (GetBackend() == PlaygroundBackend::kOpenGLES ||
1629  GetBackend() == PlaygroundBackend::kOpenGLESSDF) {
1630  GTEST_SKIP() << "Rendering to non-zero mip levels is gated on a GLES "
1631  "capability; covered by the Metal and Vulkan backends.";
1632  }
1633  auto context = GetContext();
1634  ASSERT_TRUE(context);
1635 
1636  TextureDescriptor desc;
1639  desc.size = {100, 100};
1640  desc.mip_count = 2u;
1642  auto texture = context->GetResourceAllocator()->CreateTexture(desc);
1643  ASSERT_TRUE(texture);
1644 
1645  ColorAttachment color0;
1646  color0.texture = texture;
1647  color0.mip_level = 1u;
1650  color0.clear_color = Color::Green();
1651  RenderTarget target;
1652  target.SetColorAttachment(color0, 0u);
1653  // The render area follows the mip level dimensions.
1654  EXPECT_EQ(target.GetRenderTargetSize(), ISize(50, 50));
1655 
1656  auto buffer = context->CreateCommandBuffer();
1657  auto pass = buffer->CreateRenderPass(target);
1658  ASSERT_TRUE(pass && pass->IsValid());
1659  pass->EncodeCommands();
1660  EXPECT_TRUE(context->GetCommandQueue()->Submit({buffer}).ok());
1661 }
1662 
1663 // Attachment validation rejects out-of-range mip levels and slices.
1664 TEST_P(RendererTest, AttachmentRejectsOutOfRangeSubresource) {
1665  auto context = GetContext();
1666  ASSERT_TRUE(context);
1667 
1668  TextureDescriptor desc;
1671  desc.size = {100, 100};
1672  desc.mip_count = 2u;
1674  auto texture = context->GetResourceAllocator()->CreateTexture(desc);
1675  ASSERT_TRUE(texture);
1676 
1677  ColorAttachment color0;
1678  color0.texture = texture;
1681  EXPECT_TRUE(color0.IsValid());
1682 
1683  // The out-of-range cases log validation errors on purpose.
1684  ScopedValidationDisable disable_validation;
1685 
1686  color0.mip_level = 2u; // Only levels 0 and 1 exist.
1687  EXPECT_FALSE(color0.IsValid());
1688 
1689  color0.mip_level = 0u;
1690  color0.slice = 1u; // A 2D texture has a single slice.
1691  EXPECT_FALSE(color0.IsValid());
1692 }
1693 
1694 } // namespace testing
1695 } // namespace impeller
1696 
1697 // NOLINTEND(bugprone-unchecked-optional-access)
static BufferView AsBufferView(std::shared_ptr< DeviceBuffer > buffer)
Create a buffer view of this entire buffer.
static std::shared_ptr< HostBuffer > Create(const std::shared_ptr< Allocator > &allocator, const std::shared_ptr< const IdleWaiter > &idle_waiter, size_t minimum_uniform_alignment)
Definition: host_buffer.cc:21
std::function< bool(RenderTarget &render_target)> RenderCallback
Definition: playground.h:70
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:30
virtual bool SetVertexBuffer(VertexBuffer buffer)
Specify the vertex and index buffer to use for this command.
Definition: render_pass.cc:127
const Matrix & GetOrthographicTransform() const
Definition: render_pass.cc:51
virtual void SetPipeline(PipelineRef pipeline)
The pipeline to use for this command.
Definition: render_pass.cc:86
ISize GetRenderTargetSize() const
Definition: render_pass.cc:47
virtual void SetInstanceCount(size_t count)
Definition: render_pass.cc:123
virtual fml::Status Draw()
Record the currently pending command.
Definition: render_pass.cc:208
virtual void SetCommandLabel(std::string_view label)
The debugging label to use for the command.
Definition: render_pass.cc:97
a wrapper around the impeller [Allocator] instance that can be used to provide caching of allocated r...
virtual RenderTarget CreateOffscreen(const Context &context, ISize size, int mip_count, std::string_view label="Offscreen", RenderTarget::AttachmentConfig color_attachment_config=RenderTarget::kDefaultColorAttachmentConfig, std::optional< RenderTarget::AttachmentConfig > stencil_attachment_config=RenderTarget::kDefaultStencilAttachmentConfig, const std::shared_ptr< Texture > &existing_color_texture=nullptr, const std::shared_ptr< Texture > &existing_depth_stencil_texture=nullptr, std::optional< PixelFormat > target_pixel_format=std::nullopt)
RenderTarget & SetColorAttachment(const ColorAttachment &attachment, size_t index)
ISize GetRenderTargetSize() const
RenderTarget & SetStencilAttachment(std::optional< StencilAttachment > attachment)
const std::optional< DepthAttachment > & GetDepthAttachment() const
VertexBuffer CreateVertexBuffer(HostBuffer &data_host_buffer, HostBuffer &indexes_host_buffer) const
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
void SetLabel(const std::string &label)
constexpr impeller::IndexType GetIndexType() const
VertexBufferBuilder & AppendIndex(IndexType_ index)
A wrapper around a raw ptr that adds additional unopt mode only checks.
Definition: raw_ptr.h:15
CompareFunction FunctionOf(int index) const
int IndexOf(CompareFunction func) const
int32_t value
int32_t x
std::shared_ptr< Pipeline< PipelineDescriptor > > CreateDefaultPipeline(const std::shared_ptr< Context > &context)
static const CompareFunctionUIData & CompareFunctionUI()
TEST_P(AiksTest, DrawAtlasNoColor)
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
Point Vector2
Definition: point.h:430
float Scalar
Definition: scalar.h:19
@ kNone
Does not use the index buffer.
TPoint< Scalar > Point
Definition: point.h:426
LinePipeline::FragmentShader FS
CompareFunction
Definition: formats.h:804
@ kEqual
Comparison test passes if new_value == current_value.
@ kLessEqual
Comparison test passes if new_value <= current_value.
@ kGreaterEqual
Comparison test passes if new_value >= current_value.
@ kAlways
Comparison test passes always passes.
@ kLess
Comparison test passes if new_value < current_value.
@ kGreater
Comparison test passes if new_value > current_value.
@ kNotEqual
Comparison test passes if new_value != current_value.
@ kNever
Comparison test never passes.
MipFilter
Options for selecting and filtering between mipmap levels.
Definition: formats.h:602
@ kLinear
Sample from the two nearest mip levels and linearly interpolate.
@ kBase
The texture is sampled as if it only had a single mipmap level.
@ kNearest
The nearst mipmap level is selected.
LinePipeline::VertexShader VS
MinMagFilter
Describes how the texture should be sampled when the texture is being shrunk (minified) or expanded (...
Definition: formats.h:592
@ kNearest
Select nearest to the sample point. Most widely supported.
std::shared_ptr< ContextGLES > context
std::shared_ptr< RenderPass > render_pass
std::shared_ptr< PipelineGLES > pipeline
std::shared_ptr< CommandBuffer > command_buffer
uint32_t mip_level
Definition: formats.h:915
bool IsValid() const
Definition: formats.cc:26
LoadAction load_action
Definition: formats.h:911
std::shared_ptr< Texture > texture
Definition: formats.h:909
StoreAction store_action
Definition: formats.h:912
static constexpr Color Red()
Definition: color.h:277
static Color Random()
Definition: color.h:855
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition: color.h:152
static constexpr Color Yellow()
Definition: color.h:847
static constexpr Color Blue()
Definition: color.h:281
static constexpr Color Green()
Definition: color.h:279
Scalar degrees
Definition: scalar.h:67
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:641
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:208
static Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:650
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:223
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
static Matrix MakeRotationX(Radians r)
Definition: matrix.h:193
An optional (but highly recommended) utility for creating pipelines from reflected shader information...
static std::optional< PipelineDescriptor > MakeDefaultPipelineDescriptor(const Context &context, const std::vector< Scalar > &constants={})
Create a default pipeline descriptor using the combination reflected shader information....
SamplerAddressMode width_address_mode
SamplerAddressMode height_address_mode
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
#define VALIDATION_LOG
Definition: validation.h:91