Flutter Impeller
render_pass_gles_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 <memory>
6 #include "flutter/testing/testing.h" // IWYU pragma: keep
7 #include "gmock/gmock.h"
8 #include "gtest/gtest.h"
10 #include "impeller/core/formats.h"
16 #include "impeller/renderer/backend/gles/test/mock_gles.h"
22 
23 namespace impeller {
24 namespace testing {
25 
26 using ::testing::_;
27 using ::testing::Args;
28 using ::testing::ElementsAreArray;
29 using ::testing::NiceMock;
30 using ::testing::Return;
31 using ::testing::SetArgPointee;
32 using ::testing::TestWithParam;
33 
34 class TestReactorGLES : public ReactorGLES {
35  public:
37  : ReactorGLES(std::make_unique<ProcTableGLES>(kMockResolverGLES)) {}
38 
39  ~TestReactorGLES() = default;
40 };
41 
42 class MockWorker final : public ReactorGLES::Worker {
43  public:
44  MockWorker() = default;
45 
46  // |ReactorGLES::Worker|
48  const ReactorGLES& reactor) const override {
49  return true;
50  }
51 };
52 
55  std::array<GLenum, 3> expected_attachments;
56 };
57 
59  : public TestWithParam<DiscardFrameBufferParams> {};
60 
61 namespace {
62 std::shared_ptr<ContextGLES> CreateFakeGLESContext(
63  ProcTableGLES::Resolver resolver = kMockResolverGLES) {
64  auto dummy_gl_procs = std::make_unique<ProcTableGLES>(std::move(resolver));
65  auto dummy_shader_library = std::vector<std::shared_ptr<fml::Mapping>>{};
66  auto flags = Flags{};
67  return ContextGLES::Create(flags, std::move(dummy_gl_procs),
68  dummy_shader_library, false);
69 }
70 
71 struct RenderPassGLESContext {
72  std::shared_ptr<MockGLES> mock_gl;
73  testing::NiceMock<MockGLESImpl>& mock_gl_impl_ref;
74  std::shared_ptr<ContextGLES> context;
75  std::shared_ptr<MockWorker> dummy_worker;
76  std::shared_ptr<ReactorGLES> reactor;
77  std::shared_ptr<CommandBuffer> command_buffer;
78  std::shared_ptr<RenderPass> render_pass;
79  std::shared_ptr<PipelineGLES> pipeline;
80 };
81 } // namespace
82 
84  auto mock_gl_impl = std::make_unique<NiceMock<MockGLESImpl>>();
85  auto& mock_gl_impl_ref = *mock_gl_impl;
86  auto mock_gl =
87  MockGLES::Init(std::move(mock_gl_impl), {{"GL_EXT_discard_framebuffer"}},
88  "OpenGL ES 2.0");
89 
90  auto context = CreateFakeGLESContext();
91  auto dummy_worker = std::make_shared<MockWorker>();
92  context->AddReactorWorker(dummy_worker);
93  auto reactor = context->GetReactor();
94 
95  const auto command_buffer =
96  std::static_pointer_cast<Context>(context)->CreateCommandBuffer();
97  auto render_target = RenderTarget{};
98  const auto description = TextureDescriptor{
99  .format = PixelFormat::kR8G8B8A8UNormInt, .size = {10, 10}};
100 
101  const auto& test_params = GetParam();
102  auto framebuffer_texture =
103  TextureGLES::WrapFBO(reactor, description, test_params.frame_buffer_id);
104 
105  auto color_attachment = ColorAttachment{Attachment{
106  .texture = framebuffer_texture, .store_action = StoreAction::kDontCare}};
107  render_target.SetColorAttachment(color_attachment, 0);
108  const auto render_pass = command_buffer->CreateRenderPass(render_target);
109 
110  EXPECT_CALL(mock_gl_impl_ref, GetIntegerv(GL_FRAMEBUFFER_BINDING, _))
111  .WillOnce(SetArgPointee<1>(test_params.frame_buffer_id));
112 
113  EXPECT_CALL(mock_gl_impl_ref, DiscardFramebufferEXT(GL_FRAMEBUFFER, _, _))
114  .With(Args<2, 1>(ElementsAreArray(test_params.expected_attachments)))
115  .Times(1);
116  ASSERT_TRUE(render_pass->EncodeCommands());
117  ASSERT_TRUE(reactor->React());
118 }
119 
121  FrameBufferObject,
123  ::testing::ValuesIn(std::vector<DiscardFrameBufferParams>{
124  {.frame_buffer_id = 0,
125  .expected_attachments = {GL_COLOR_EXT, GL_DEPTH_EXT, GL_STENCIL_EXT}},
126  {.frame_buffer_id = 1,
127  .expected_attachments = {GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT,
128  GL_STENCIL_ATTACHMENT}}}),
129  [](const ::testing::TestParamInfo<DiscardFrameBufferParams>& info) {
130  return (info.param.frame_buffer_id == 0) ? "Default" : "NonDefault";
131  });
132 
134  auto mock_gl_impl = std::make_unique<NiceMock<MockGLESImpl>>();
135  auto& mock_gl_impl_ref = *mock_gl_impl;
136  auto mock_gl =
137  MockGLES::Init(std::move(mock_gl_impl), std::nullopt, "OpenGL ES 3.0");
138 
139  auto context = CreateFakeGLESContext();
140  auto dummy_worker = std::make_shared<MockWorker>();
141  context->AddReactorWorker(dummy_worker);
142  auto reactor = context->GetReactor();
143 
144  const auto command_buffer =
145  std::static_pointer_cast<Context>(context)->CreateCommandBuffer();
146  auto render_target = RenderTarget{};
147  const auto description = TextureDescriptor{
148  .format = PixelFormat::kR8G8B8A8UNormInt, .size = {10, 10}};
149 
150  const auto& test_params = GetParam();
151  auto framebuffer_texture =
152  TextureGLES::WrapFBO(reactor, description, test_params.frame_buffer_id);
153 
154  auto color_attachment = ColorAttachment{Attachment{
155  .texture = framebuffer_texture, .store_action = StoreAction::kDontCare}};
156  render_target.SetColorAttachment(color_attachment, 0);
157  const auto render_pass = command_buffer->CreateRenderPass(render_target);
158 
159  EXPECT_CALL(mock_gl_impl_ref, GetIntegerv(GL_FRAMEBUFFER_BINDING, _))
160  .WillOnce(SetArgPointee<1>(test_params.frame_buffer_id));
161 
162  // InvalidateFramebuffer should be called instead of DiscardFramebufferEXT
163  EXPECT_CALL(mock_gl_impl_ref, InvalidateFramebuffer(GL_FRAMEBUFFER, _, _))
164  .With(Args<2, 1>(ElementsAreArray(test_params.expected_attachments)))
165  .Times(1);
166  EXPECT_CALL(mock_gl_impl_ref, DiscardFramebufferEXT(GL_FRAMEBUFFER, _, _))
167  .Times(0);
168 
169  ASSERT_TRUE(render_pass->EncodeCommands());
170  ASSERT_TRUE(reactor->React());
171 }
172 
173 TEST(RenderPassGLESTest, ResolvingMultisampleTextureCachesResolveFBO) {
174  auto mock_gl_impl = std::make_unique<NiceMock<MockGLESImpl>>();
175  auto& mock_gl_impl_ref = *mock_gl_impl;
176  // Make sure implicit resolving isn't supported so we go down explicit path.
177  auto mock_gl =
178  MockGLES::Init(std::move(mock_gl_impl), std::nullopt, "OpenGL ES 3.0");
179 
180  auto context = CreateFakeGLESContext();
181  auto dummy_worker = std::make_shared<MockWorker>();
182  context->AddReactorWorker(dummy_worker);
183  auto reactor = context->GetReactor();
184 
185  const auto command_buffer =
186  std::static_pointer_cast<Context>(context)->CreateCommandBuffer();
187 
188  const auto msaa_desc =
191  .size = {10, 10},
193  .sample_count = SampleCount::kCount4};
194  const auto resolve_desc =
196  .type = TextureType::kTexture2D,
198  .size = {10, 10},
200  .sample_count = SampleCount::kCount1};
201 
202  auto msaa_tex = std::make_shared<TextureGLES>(reactor, msaa_desc);
203  auto resolve_tex = std::make_shared<TextureGLES>(reactor, resolve_desc);
204 
205  auto render_target = RenderTarget{};
206  auto color_attachment = ColorAttachment{Attachment{
207  .texture = msaa_tex,
208  .resolve_texture = resolve_tex,
209  .load_action = LoadAction::kClear,
210  .store_action = StoreAction::kMultisampleResolve,
211  }};
212  color_attachment.clear_color = Color::Black();
213  render_target.SetColorAttachment(color_attachment, 0);
214 
215  EXPECT_CALL(mock_gl_impl_ref, CheckFramebufferStatus(_))
216  .WillRepeatedly(Return(GL_FRAMEBUFFER_COMPLETE));
217 
218  // Expect GenFramebuffers is called exactly once for the offscreen FBO,
219  // and exactly once for the resolve FBO over two passes.
220  EXPECT_CALL(mock_gl_impl_ref, GenFramebuffers(_, _)).Times(2);
221 
222  {
223  const auto render_pass = command_buffer->CreateRenderPass(render_target);
224  ASSERT_TRUE(render_pass->EncodeCommands());
225  ASSERT_TRUE(reactor->React());
226  }
227  {
228  const auto render_pass2 = command_buffer->CreateRenderPass(render_target);
229  ASSERT_TRUE(render_pass2->EncodeCommands());
230  ASSERT_TRUE(reactor->React());
231  }
232 }
233 
234 class RenderPassGLESCommandTest : public ::testing::Test {
235  protected:
236  // Builds a mock OpenGL ES context with a render pass and a minimal
237  // pipeline. The [resolver] controls which GL entry points the backend can
238  // see, which is how a caller selects the hardware or emulated instancing
239  // path.
240  static RenderPassGLESContext CreateRenderPassGLESContext(
241  ProcTableGLES::Resolver resolver = kMockResolverGLES) {
242  std::unique_ptr<NiceMock<MockGLESImpl>> mock_gl_impl =
243  std::make_unique<NiceMock<MockGLESImpl>>();
244  testing::NiceMock<MockGLESImpl>& mock_gl_impl_ref = *mock_gl_impl;
245  std::shared_ptr<MockGLES> mock_gl = MockGLES::Init(std::move(mock_gl_impl));
246 
247  std::shared_ptr<ContextGLES> context =
248  CreateFakeGLESContext(std::move(resolver));
249  std::shared_ptr<MockWorker> dummy_worker = std::make_shared<MockWorker>();
250  context->AddReactorWorker(dummy_worker);
251  std::shared_ptr<ReactorGLES> reactor = context->GetReactor();
252 
253  TextureDescriptor tex_desc;
254  tex_desc.size = {100, 100};
256  auto texture = std::make_shared<TextureGLES>(reactor, tex_desc, false);
257 
258  RenderTarget target;
259  ColorAttachment color0;
260  color0.texture = texture;
263  target.SetColorAttachment(color0, 0);
264 
265  std::shared_ptr<CommandBuffer> command_buffer =
266  std::static_pointer_cast<Context>(context)->CreateCommandBuffer();
267  std::shared_ptr<RenderPass> render_pass =
268  command_buffer->CreateRenderPass(target);
269 
270  EXPECT_CALL(mock_gl_impl_ref, CheckFramebufferStatus(_))
271  .WillRepeatedly(Return(GL_FRAMEBUFFER_COMPLETE));
272 
273  PipelineDescriptor desc;
274  ColorAttachmentDescriptor color0_desc;
276  desc.SetColorAttachmentDescriptor(0, color0_desc);
277 
278  HandleGLES pipeline_handle = reactor->CreateHandle(HandleType::kProgram);
279  std::shared_ptr<PipelineGLES> pipeline =
280  std::shared_ptr<PipelineGLES>(new PipelineGLES(
281  reactor, std::weak_ptr<PipelineLibrary>(), desc,
282  std::make_shared<UniqueHandleGLES>(reactor, pipeline_handle)));
283  pipeline->buffer_bindings_ = std::make_unique<BufferBindingsGLES>();
284 
285  return {std::move(mock_gl), mock_gl_impl_ref,
286  std::move(context), std::move(dummy_worker),
287  std::move(reactor), std::move(command_buffer),
288  std::move(render_pass), std::move(pipeline)};
289  }
290 };
291 
292 TEST_F(RenderPassGLESCommandTest, ViewportCachedAcrossCommands) {
293  auto ctx = CreateRenderPassGLESContext();
294  testing::NiceMock<MockGLESImpl>& mock_gl_impl_ref = ctx.mock_gl_impl_ref;
295  std::shared_ptr<RenderPass>& render_pass = ctx.render_pass;
296  std::shared_ptr<PipelineGLES>& pipeline = ctx.pipeline;
297  std::shared_ptr<ReactorGLES>& reactor = ctx.reactor;
298 
299  render_pass->SetPipeline(PipelineRef(pipeline));
300  render_pass->SetElementCount(1);
301  render_pass->SetIndexBuffer({}, IndexType::kNone);
302  EXPECT_TRUE(render_pass->Draw().ok());
303 
304  render_pass->SetPipeline(PipelineRef(pipeline));
305  render_pass->SetElementCount(1);
306  render_pass->SetIndexBuffer({}, IndexType::kNone);
307  render_pass->SetViewport(
308  Viewport{Rect::MakeXYWH(0, 0, 50, 50), DepthRange{0.0f, 1.0f}});
309  EXPECT_TRUE(render_pass->Draw().ok());
310 
311  render_pass->SetPipeline(PipelineRef(pipeline));
312  render_pass->SetElementCount(1);
313  render_pass->SetIndexBuffer({}, IndexType::kNone);
314  render_pass->SetViewport(
315  Viewport{Rect::MakeXYWH(0, 0, 50, 50), DepthRange{0.0f, 1.0f}});
316  EXPECT_TRUE(render_pass->Draw().ok());
317 
318  // Viewport should only be called twice. Once for the fallback, once for the
319  // first override. We set a catch-all to 0 to ensure no other calls occur.
320  EXPECT_CALL(mock_gl_impl_ref, Viewport(_, _, _, _)).Times(0);
321  EXPECT_CALL(mock_gl_impl_ref, Viewport(0, 0, 100, 100)).Times(1);
322  EXPECT_CALL(mock_gl_impl_ref, Viewport(0, 0, 50, 50)).Times(1);
323 
324  EXPECT_TRUE(render_pass->EncodeCommands());
325  EXPECT_TRUE(reactor->React());
326 }
327 
329  CommandsWithoutViewportGetRenderPassViewport) {
330  auto ctx = CreateRenderPassGLESContext();
331  testing::NiceMock<MockGLESImpl>& mock_gl_impl_ref = ctx.mock_gl_impl_ref;
332  std::shared_ptr<RenderPass>& render_pass = ctx.render_pass;
333  std::shared_ptr<PipelineGLES>& pipeline = ctx.pipeline;
334  std::shared_ptr<ReactorGLES>& reactor = ctx.reactor;
335 
336  render_pass->SetPipeline(PipelineRef(pipeline));
337  render_pass->SetElementCount(1);
338  render_pass->SetIndexBuffer({}, IndexType::kNone);
339  EXPECT_TRUE(render_pass->Draw().ok());
340 
341  render_pass->SetPipeline(PipelineRef(pipeline));
342  render_pass->SetElementCount(1);
343  render_pass->SetIndexBuffer({}, IndexType::kNone);
344  render_pass->SetViewport(
345  Viewport{Rect::MakeXYWH(0, 0, 50, 50), DepthRange{0.0f, 1.0f}});
346  EXPECT_TRUE(render_pass->Draw().ok());
347 
348  render_pass->SetPipeline(PipelineRef(pipeline));
349  render_pass->SetElementCount(1);
350  render_pass->SetIndexBuffer({}, IndexType::kNone);
351  EXPECT_TRUE(render_pass->Draw().ok());
352 
353  EXPECT_CALL(mock_gl_impl_ref, Viewport(_, _, _, _)).Times(0);
354  EXPECT_CALL(mock_gl_impl_ref, Viewport(0, 0, 100, 100)).Times(2);
355  EXPECT_CALL(mock_gl_impl_ref, Viewport(0, 0, 50, 50)).Times(1);
356 
357  EXPECT_TRUE(render_pass->EncodeCommands());
358  EXPECT_TRUE(reactor->React());
359 }
360 
361 // Sibling regression guard for the bug fixed alongside this test on the
362 // Vulkan backend. The GLES backend has always honored the X offset; this
363 // asserts that explicitly so a future change can't silently regress it.
364 TEST_F(RenderPassGLESCommandTest, ViewportWithNonZeroXOffsetReachesGL) {
365  auto ctx = CreateRenderPassGLESContext();
366  testing::NiceMock<MockGLESImpl>& mock_gl_impl_ref = ctx.mock_gl_impl_ref;
367  std::shared_ptr<RenderPass>& render_pass = ctx.render_pass;
368  std::shared_ptr<PipelineGLES>& pipeline = ctx.pipeline;
369  std::shared_ptr<ReactorGLES>& reactor = ctx.reactor;
370 
371  render_pass->SetPipeline(PipelineRef(pipeline));
372  render_pass->SetElementCount(1);
373  render_pass->SetIndexBuffer({}, IndexType::kNone);
374  render_pass->SetViewport(
375  Viewport{Rect::MakeXYWH(25, 0, 50, 100), DepthRange{0.0f, 1.0f}});
376  EXPECT_TRUE(render_pass->Draw().ok());
377 
378  EXPECT_CALL(mock_gl_impl_ref, Viewport(_, _, _, _)).Times(0);
379  EXPECT_CALL(mock_gl_impl_ref, Viewport(25, 0, 50, 100)).Times(1);
380 
381  EXPECT_TRUE(render_pass->EncodeCommands());
382  EXPECT_TRUE(reactor->React());
383 }
384 
385 // When the driver exposes the hardware instancing entry points, a
386 // non-indexed instanced command issues a single glDrawArraysInstanced call
387 // that carries the instance count.
388 TEST_F(RenderPassGLESCommandTest, HardwareInstancedArrayDraw) {
389  auto ctx = CreateRenderPassGLESContext();
390  testing::NiceMock<MockGLESImpl>& mock_gl_impl_ref = ctx.mock_gl_impl_ref;
391  std::shared_ptr<RenderPass>& render_pass = ctx.render_pass;
392  std::shared_ptr<PipelineGLES>& pipeline = ctx.pipeline;
393  std::shared_ptr<ReactorGLES>& reactor = ctx.reactor;
394 
395  render_pass->SetPipeline(PipelineRef(pipeline));
396  render_pass->SetElementCount(3);
397  render_pass->SetIndexBuffer({}, IndexType::kNone);
398  render_pass->SetInstanceCount(4);
399  EXPECT_TRUE(render_pass->Draw().ok());
400 
401  EXPECT_CALL(mock_gl_impl_ref,
402  DrawArraysInstanced(/*mode=*/_, /*first=*/0, /*count=*/3,
403  /*instancecount=*/4))
404  .Times(1);
405  EXPECT_CALL(mock_gl_impl_ref, DrawArrays(_, _, _)).Times(0);
406 
407  EXPECT_TRUE(render_pass->EncodeCommands());
408  EXPECT_TRUE(reactor->React());
409 }
410 
411 // The indexed counterpart: a hardware instanced indexed command issues a
412 // single glDrawElementsInstanced call.
413 TEST_F(RenderPassGLESCommandTest, HardwareInstancedElementsDraw) {
414  auto ctx = CreateRenderPassGLESContext();
415  testing::NiceMock<MockGLESImpl>& mock_gl_impl_ref = ctx.mock_gl_impl_ref;
416  std::shared_ptr<RenderPass>& render_pass = ctx.render_pass;
417  std::shared_ptr<PipelineGLES>& pipeline = ctx.pipeline;
418  std::shared_ptr<ReactorGLES>& reactor = ctx.reactor;
419 
420  DeviceBufferDescriptor index_desc;
421  index_desc.size = 6 * sizeof(uint16_t);
423  auto index_buffer = std::static_pointer_cast<Context>(ctx.context)
424  ->GetResourceAllocator()
425  ->CreateBuffer(index_desc);
426  ASSERT_TRUE(index_buffer);
427 
428  render_pass->SetPipeline(PipelineRef(pipeline));
429  render_pass->SetElementCount(6);
430  ASSERT_TRUE(render_pass->SetIndexBuffer(
432  render_pass->SetInstanceCount(4);
433  EXPECT_TRUE(render_pass->Draw().ok());
434 
435  EXPECT_CALL(mock_gl_impl_ref,
436  DrawElementsInstanced(/*mode=*/_, /*count=*/6, /*type=*/_,
437  /*indices=*/_, /*instancecount=*/4))
438  .Times(1);
439  EXPECT_CALL(mock_gl_impl_ref, DrawElements(_, _, _, _)).Times(0);
440 
441  EXPECT_TRUE(render_pass->EncodeCommands());
442  EXPECT_TRUE(reactor->React());
443 }
444 
445 // When the hardware instancing entry points are missing, a non-indexed
446 // instanced command is emulated by repeating the plain draw once per
447 // instance.
448 TEST_F(RenderPassGLESCommandTest, EmulatedInstancedArrayDraw) {
449  auto ctx = CreateRenderPassGLESContext(kMockResolverGLESWithoutInstancing);
450  testing::NiceMock<MockGLESImpl>& mock_gl_impl_ref = ctx.mock_gl_impl_ref;
451  std::shared_ptr<RenderPass>& render_pass = ctx.render_pass;
452  std::shared_ptr<PipelineGLES>& pipeline = ctx.pipeline;
453  std::shared_ptr<ReactorGLES>& reactor = ctx.reactor;
454 
455  render_pass->SetPipeline(PipelineRef(pipeline));
456  render_pass->SetElementCount(3);
457  render_pass->SetIndexBuffer({}, IndexType::kNone);
458  render_pass->SetInstanceCount(4);
459  EXPECT_TRUE(render_pass->Draw().ok());
460 
461  EXPECT_CALL(mock_gl_impl_ref,
462  DrawArrays(/*mode=*/_, /*first=*/0, /*count=*/3))
463  .Times(4);
464  EXPECT_CALL(mock_gl_impl_ref, DrawArraysInstanced(_, _, _, _)).Times(0);
465 
466  EXPECT_TRUE(render_pass->EncodeCommands());
467  EXPECT_TRUE(reactor->React());
468 }
469 
470 // The indexed counterpart of the emulation path: the plain indexed draw is
471 // repeated once per instance.
472 TEST_F(RenderPassGLESCommandTest, EmulatedInstancedElementsDraw) {
473  auto ctx = CreateRenderPassGLESContext(kMockResolverGLESWithoutInstancing);
474  testing::NiceMock<MockGLESImpl>& mock_gl_impl_ref = ctx.mock_gl_impl_ref;
475  std::shared_ptr<RenderPass>& render_pass = ctx.render_pass;
476  std::shared_ptr<PipelineGLES>& pipeline = ctx.pipeline;
477  std::shared_ptr<ReactorGLES>& reactor = ctx.reactor;
478 
479  DeviceBufferDescriptor index_desc;
480  index_desc.size = 6 * sizeof(uint16_t);
482  auto index_buffer = std::static_pointer_cast<Context>(ctx.context)
483  ->GetResourceAllocator()
484  ->CreateBuffer(index_desc);
485  ASSERT_TRUE(index_buffer);
486 
487  render_pass->SetPipeline(PipelineRef(pipeline));
488  render_pass->SetElementCount(6);
489  ASSERT_TRUE(render_pass->SetIndexBuffer(
491  render_pass->SetInstanceCount(4);
492  EXPECT_TRUE(render_pass->Draw().ok());
493 
494  EXPECT_CALL(mock_gl_impl_ref,
495  DrawElements(/*mode=*/_, /*count=*/6, /*type=*/_, /*indices=*/_))
496  .Times(4);
497  EXPECT_CALL(mock_gl_impl_ref, DrawElementsInstanced(_, _, _, _, _)).Times(0);
498 
499  EXPECT_TRUE(render_pass->EncodeCommands());
500  EXPECT_TRUE(reactor->React());
501 }
502 
503 // Regression guard: a command with no instance count set draws a single
504 // instance through the plain, non-instanced entry point.
505 TEST_F(RenderPassGLESCommandTest, NonInstancedDrawIssuesSingleDrawArrays) {
506  auto ctx = CreateRenderPassGLESContext();
507  testing::NiceMock<MockGLESImpl>& mock_gl_impl_ref = ctx.mock_gl_impl_ref;
508  std::shared_ptr<RenderPass>& render_pass = ctx.render_pass;
509  std::shared_ptr<PipelineGLES>& pipeline = ctx.pipeline;
510  std::shared_ptr<ReactorGLES>& reactor = ctx.reactor;
511 
512  render_pass->SetPipeline(PipelineRef(pipeline));
513  render_pass->SetElementCount(3);
514  render_pass->SetIndexBuffer({}, IndexType::kNone);
515  EXPECT_TRUE(render_pass->Draw().ok());
516 
517  EXPECT_CALL(mock_gl_impl_ref,
518  DrawArrays(/*mode=*/_, /*first=*/0, /*count=*/3))
519  .Times(1);
520  EXPECT_CALL(mock_gl_impl_ref, DrawArraysInstanced(_, _, _, _)).Times(0);
521 
522  EXPECT_TRUE(render_pass->EncodeCommands());
523  EXPECT_TRUE(reactor->React());
524 }
525 
526 // A command with an explicit instance count of zero draws nothing, matching
527 // the Metal and Vulkan backends.
528 TEST_F(RenderPassGLESCommandTest, ZeroInstanceCountIssuesNoDraw) {
529  auto ctx = CreateRenderPassGLESContext();
530  testing::NiceMock<MockGLESImpl>& mock_gl_impl_ref = ctx.mock_gl_impl_ref;
531  std::shared_ptr<RenderPass>& render_pass = ctx.render_pass;
532  std::shared_ptr<PipelineGLES>& pipeline = ctx.pipeline;
533  std::shared_ptr<ReactorGLES>& reactor = ctx.reactor;
534 
535  render_pass->SetPipeline(PipelineRef(pipeline));
536  render_pass->SetElementCount(3);
537  render_pass->SetIndexBuffer({}, IndexType::kNone);
538  render_pass->SetInstanceCount(0);
539  EXPECT_TRUE(render_pass->Draw().ok());
540 
541  EXPECT_CALL(mock_gl_impl_ref, DrawArrays(_, _, _)).Times(0);
542  EXPECT_CALL(mock_gl_impl_ref, DrawArraysInstanced(_, _, _, _)).Times(0);
543 
544  EXPECT_TRUE(render_pass->EncodeCommands());
545  EXPECT_TRUE(reactor->React());
546 }
547 
548 } // namespace testing
549 } // namespace impeller
static std::shared_ptr< ContextGLES > Create(const Flags &flags, std::unique_ptr< ProcTableGLES > gl, const std::vector< std::shared_ptr< fml::Mapping >> &shader_libraries, bool enable_gpu_tracing)
Definition: context_gles.cc:21
static BufferView AsBufferView(std::shared_ptr< DeviceBuffer > buffer)
Create a buffer view of this entire buffer.
Represents a handle to an underlying OpenGL object. Unlike OpenGL object handles, these handles can b...
Definition: handle_gles.h:42
PipelineDescriptor & SetColorAttachmentDescriptor(size_t index, ColorAttachmentDescriptor desc)
std::function< void *(const char *function_name)> Resolver
A delegate implemented by a thread on which an OpenGL context is current. There may be multiple worke...
Definition: reactor_gles.h:69
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
Definition: reactor_gles.h:57
RenderTarget & SetColorAttachment(const ColorAttachment &attachment, size_t index)
static std::shared_ptr< TextureGLES > WrapFBO(std::shared_ptr< ReactorGLES > reactor, TextureDescriptor desc, GLuint fbo)
Create a texture by wrapping an external framebuffer object whose lifecycle is owned by the caller.
Definition: texture_gles.cc:95
bool CanReactorReactOnCurrentThreadNow(const ReactorGLES &reactor) const override
Determines the ability of the worker to service a reaction on the current thread. The OpenGL context ...
static RenderPassGLESContext CreateRenderPassGLESContext(ProcTableGLES::Resolver resolver=kMockResolverGLES)
TEST(AllocationSizeTest, CanCreateTypedAllocations)
TEST_P(AiksTest, DrawAtlasNoColor)
TEST_F(GoldenTests, ConicalGradient)
Definition: golden_tests.cc:80
INSTANTIATE_TEST_SUITE_P(FrameBufferObject, RenderPassGLESWithDiscardFrameBufferExtTest, ::testing::ValuesIn(std::vector< DiscardFrameBufferParams >{ {.frame_buffer_id=0,.expected_attachments={GL_COLOR_EXT, GL_DEPTH_EXT, GL_STENCIL_EXT}}, {.frame_buffer_id=1,.expected_attachments={GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT}}}), [](const ::testing::TestParamInfo< DiscardFrameBufferParams > &info) { return(info.param.frame_buffer_id==0) ? "Default" :"NonDefault";})
@ kNone
Does not use the index buffer.
raw_ptr< Pipeline< PipelineDescriptor > > PipelineRef
A raw ptr to a pipeline object.
Definition: pipeline.h:89
Definition: comparable.h:93
std::shared_ptr< MockWorker > dummy_worker
std::shared_ptr< ReactorGLES > reactor
std::shared_ptr< ContextGLES > context
std::shared_ptr< RenderPass > render_pass
testing::NiceMock< MockGLESImpl > & mock_gl_impl_ref
std::shared_ptr< PipelineGLES > pipeline
std::shared_ptr< MockGLES > mock_gl
std::shared_ptr< CommandBuffer > command_buffer
LoadAction load_action
Definition: formats.h:911
std::shared_ptr< Texture > texture
Definition: formats.h:909
StoreAction store_action
Definition: formats.h:912
Describe the color attachment that will be used with this pipeline.
Definition: formats.h:770
static constexpr Color Black()
Definition: color.h:271
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:136
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...