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