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