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