Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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"
22
23namespace impeller {
24namespace testing {
25
26using ::testing::_;
27using ::testing::Args;
28using ::testing::ElementsAreArray;
29using ::testing::NiceMock;
30using ::testing::Return;
31using ::testing::SetArgPointee;
32using ::testing::TestWithParam;
33
34class TestReactorGLES : public ReactorGLES {
35 public:
38
39 ~TestReactorGLES() = default;
40};
41
42class 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
61namespace {
62std::shared_ptr<ContextGLES> CreateFakeGLESContext(
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
71struct 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
173TEST(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 =
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
234class 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(
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
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
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
292TEST_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.
364TEST_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.
388TEST_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.
413TEST_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.
448TEST_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.
472TEST_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.
505TEST_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.
528TEST_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)
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...
The reactor attempts to make thread-safe usage of OpenGL ES easier to reason about.
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.
static std::shared_ptr< MockGLES > Init(std::unique_ptr< MockGLESImpl > impl, const std::optional< std::vector< const char * > > &extensions=std::nullopt, const char *version_string="OpenGL ES 3.0")
Definition mock_gles.cc:417
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)
uint32_t * target
FlTexture * texture
TEST(FrameTimingsRecorderTest, RecordVsync)
const ProcTableGLES::Resolver kMockResolverGLESWithoutInstancing
Definition mock_gles.cc:545
TEST_P(AiksTest, DrawAtlasNoColor)
const ProcTableGLES::Resolver kMockResolverGLES
Definition mock_gles.cc:459
TEST_F(GoldenTests, ConicalGradient)
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 ref_ptr.h:261
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
static constexpr 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...