| Flutter Impeller |

renderer_unittests.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/fml/logging.h"
6
7 #include "impeller/core/device_buffer_descriptor.h"
8 #include "impeller/core/formats.h"
9 #include "impeller/core/sampler_descriptor.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/geometry/path_builder.h"
25 #include "impeller/playground/playground_test.h"
26 #include "impeller/renderer/command.h"
27 #include "impeller/renderer/command_buffer.h"
28 #include "impeller/renderer/pipeline_builder.h"
29 #include "impeller/renderer/pipeline_library.h"
30 #include "impeller/renderer/render_pass.h"
31 #include "impeller/renderer/render_target.h"
32 #include "impeller/renderer/renderer.h"
33 #include "impeller/renderer/vertex_buffer_builder.h"
34 #include "impeller/tessellator/tessellator.h"
35 #include "third_party/imgui/imgui.h"
36
37 // TODO(zanderso): https://github.com/flutter/flutter/issues/127701
38 // NOLINTBEGIN(bugprone-unchecked-optional-access)
39
40 namespace impeller {
41 namespace testing {
42
43 using RendererTest = PlaygroundTest;
44 INSTANTIATE_PLAYGROUND_SUITE(RendererTest);
45
46 TEST_P(RendererTest, CanCreateBoxPrimitive) {
47 using VS = BoxFadeVertexShader;
48 using FS = BoxFadeFragmentShader;
49 auto context = GetContext();
50 ASSERT_TRUE(context);
51 using BoxPipelineBuilder = PipelineBuilder<VS, FS>;
52 auto desc = BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context);
53 ASSERT_TRUE(desc.has_value());
54 desc->SetSampleCount(SampleCount::kCount4);
55 desc->SetStencilAttachmentDescriptors(std::nullopt);
56
57 // Vertex buffer.
58 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
59 vertex_builder.SetLabel("Box");
60 vertex_builder.AddVertices({
61 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
62 {{800, 100, 0.0}, {1.0, 0.0}}, // 2
63 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
64 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
65 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
66 {{100, 800, 0.0}, {0.0, 1.0}}, // 4
67 });
68 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
69 auto boston = CreateTextureForFixture("boston.jpg");
70 ASSERT_TRUE(bridge && boston);
71 auto sampler = context->GetSamplerLibrary()->GetSampler({});
72 ASSERT_TRUE(sampler);
73 SinglePassCallback callback = [&](RenderPass& pass) {
74 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
75 static bool wireframe;
76 ImGui::Checkbox("Wireframe", &wireframe);
77 ImGui::End();
78
79 desc->SetPolygonMode(wireframe ? PolygonMode::kLine : PolygonMode::kFill);
80 auto pipeline = context->GetPipelineLibrary()->GetPipeline(desc).Get();
81
82 assert(pipeline && pipeline->IsValid());
83
84 Command cmd;
85 DEBUG_COMMAND_INFO(cmd, "Box");
86 cmd.pipeline = pipeline;
87
88 cmd.BindVertices(
89 vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator()));
90
91 VS::UniformBuffer uniforms;
92 EXPECT_EQ(pass.GetOrthographicTransform(),
93 Matrix::MakeOrthographic(pass.GetRenderTargetSize()));
94 uniforms.mvp =
95 pass.GetOrthographicTransform() * Matrix::MakeScale(GetContentScale());
96 VS::BindUniformBuffer(cmd,
97 pass.GetTransientsBuffer().EmplaceUniform(uniforms));
98
99 FS::FrameInfo frame_info;
100 frame_info.current_time = GetSecondsElapsed();
101 frame_info.cursor_position = GetCursorPosition();
102 frame_info.window_size.x = GetWindowSize().width;
103 frame_info.window_size.y = GetWindowSize().height;
104
105 FS::BindFrameInfo(cmd,
106 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
107 FS::BindContents1(cmd, boston, sampler);
108 FS::BindContents2(cmd, bridge, sampler);
109 if (!pass.AddCommand(std::move(cmd))) {
110 return false;
111 }
112 return true;
113 };
114 OpenPlaygroundHere(callback);
115 }
116
117 TEST_P(RendererTest, CanRenderPerspectiveCube) {
118 using VS = ColorsVertexShader;
119 using FS = ColorsFragmentShader;
120 auto context = GetContext();
121 ASSERT_TRUE(context);
122 auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
123 ASSERT_TRUE(desc.has_value());
124 desc->SetCullMode(CullMode::kBackFace);
125 desc->SetWindingOrder(WindingOrder::kCounterClockwise);
126 desc->SetSampleCount(SampleCount::kCount4);
127 desc->SetStencilAttachmentDescriptors(std::nullopt);
128 auto pipeline =
129 context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
130 ASSERT_TRUE(pipeline);
131
132 struct Cube {
133 VS::PerVertexData vertices[8] = {
134 // -Z
135 {{-1, -1, -1}, Color::Red()},
136 {{1, -1, -1}, Color::Yellow()},
137 {{1, 1, -1}, Color::Green()},
138 {{-1, 1, -1}, Color::Blue()},
139 // +Z
140 {{-1, -1, 1}, Color::Green()},
141 {{1, -1, 1}, Color::Blue()},
142 {{1, 1, 1}, Color::Red()},
143 {{-1, 1, 1}, Color::Yellow()},
144 };
145 uint16_t indices[36] = {
146 1, 5, 2, 2, 5, 6, // +X
147 4, 0, 7, 7, 0, 3, // -X
148 4, 5, 0, 0, 5, 1, // +Y
149 3, 2, 7, 7, 2, 6, // -Y
150 5, 4, 6, 6, 4, 7, // +Z
151 0, 1, 3, 3, 1, 2, // -Z
152 };
153 } cube;
154
155 VertexBuffer vertex_buffer;
156 {
157 auto device_buffer = context->GetResourceAllocator()->CreateBufferWithCopy(
158 reinterpret_cast<uint8_t*>(&cube), sizeof(cube));
159 vertex_buffer.vertex_buffer = {
160 .buffer = device_buffer,
161 .range = Range(offsetof(Cube, vertices), sizeof(Cube::vertices))};
162 vertex_buffer.index_buffer = {
163 .buffer = device_buffer,
164 .range = Range(offsetof(Cube, indices), sizeof(Cube::indices))};
165 vertex_buffer.vertex_count = 36;
166 vertex_buffer.index_type = IndexType::k16bit;
167 }
168
169 auto sampler = context->GetSamplerLibrary()->GetSampler({});
170 ASSERT_TRUE(sampler);
171
172 Vector3 euler_angles;
173 SinglePassCallback callback = [&](RenderPass& pass) {
174 static Degrees fov_y(60);
175 static Scalar distance = 10;
176
177 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
178 ImGui::SliderFloat("Field of view", &fov_y.degrees, 0, 180);
179 ImGui::SliderFloat("Camera distance", &distance, 0, 30);
180 ImGui::End();
181
182 Command cmd;
183 DEBUG_COMMAND_INFO(cmd, "Perspective Cube");
184 cmd.pipeline = pipeline;
185
186 cmd.BindVertices(vertex_buffer);
187
188 VS::UniformBuffer uniforms;
189 Scalar time = GetSecondsElapsed();
190 euler_angles = Vector3(0.19 * time, 0.7 * time, 0.43 * time);
191
192 uniforms.mvp =
193 Matrix::MakePerspective(fov_y, pass.GetRenderTargetSize(), 0, 10) *
194 Matrix::MakeTranslation({0, 0, distance}) *
195 Matrix::MakeRotationX(Radians(euler_angles.x)) *
196 Matrix::MakeRotationY(Radians(euler_angles.y)) *
197 Matrix::MakeRotationZ(Radians(euler_angles.z));
198 VS::BindUniformBuffer(cmd,
199 pass.GetTransientsBuffer().EmplaceUniform(uniforms));
200 if (!pass.AddCommand(std::move(cmd))) {
201 return false;
202 }
203 return true;
204 };
205 OpenPlaygroundHere(callback);
206 }
207
208 TEST_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.
223 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
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 auto sampler = context->GetSamplerLibrary()->GetSampler({});
241 ASSERT_TRUE(sampler);
242
243 SinglePassCallback callback = [&](RenderPass& pass) {
244 Command cmd;
245 DEBUG_COMMAND_INFO(cmd, "Box");
246 cmd.pipeline = box_pipeline;
247
248 cmd.BindVertices(vertex_buffer);
249
250 FS::FrameInfo frame_info;
251 frame_info.current_time = GetSecondsElapsed();
252 frame_info.cursor_position = GetCursorPosition();
253 frame_info.window_size.x = GetWindowSize().width;
254 frame_info.window_size.y = GetWindowSize().height;
255
256 FS::BindFrameInfo(cmd,
257 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
258 FS::BindContents1(cmd, boston, sampler);
259 FS::BindContents2(cmd, bridge, sampler);
260
261 for (size_t i = 0; i < 1; i++) {
262 for (size_t j = 0; j < 1; j++) {
263 VS::UniformBuffer uniforms;
264 EXPECT_EQ(pass.GetOrthographicTransform(),
265 Matrix::MakeOrthographic(pass.GetRenderTargetSize()));
266 uniforms.mvp = pass.GetOrthographicTransform() *
267 Matrix::MakeScale(GetContentScale()) *
268 Matrix::MakeTranslation({i * 50.0f, j * 50.0f, 0.0f});
269 VS::BindUniformBuffer(
270 cmd, pass.GetTransientsBuffer().EmplaceUniform(uniforms));
271 if (!pass.AddCommand(std::move(cmd))) {
272 return false;
273 }
274 }
275 }
276
277 return true;
278 };
279 OpenPlaygroundHere(callback);
280 }
281
282 TEST_P(RendererTest, CanRenderToTexture) {
283 using VS = BoxFadeVertexShader;
284 using FS = BoxFadeFragmentShader;
285 auto context = GetContext();
286 ASSERT_TRUE(context);
287 using BoxPipelineBuilder = PipelineBuilder<VS, FS>;
288 auto pipeline_desc =
289 BoxPipelineBuilder::MakeDefaultPipelineDescriptor(*context);
290 pipeline_desc->SetSampleCount(SampleCount::kCount1);
291
292 ASSERT_TRUE(pipeline_desc.has_value());
293 auto box_pipeline =
294 context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
295 ASSERT_TRUE(box_pipeline);
296
297 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
298 vertex_builder.SetLabel("Box");
299 vertex_builder.AddVertices({
300 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
301 {{800, 100, 0.0}, {1.0, 0.0}}, // 2
302 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
303 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
304 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
305 {{100, 800, 0.0}, {0.0, 1.0}}, // 4
306 });
307 auto vertex_buffer =
308 vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
309 ASSERT_TRUE(vertex_buffer);
310
311 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
312 auto boston = CreateTextureForFixture("boston.jpg");
313 ASSERT_TRUE(bridge && boston);
314 auto sampler = context->GetSamplerLibrary()->GetSampler({});
315 ASSERT_TRUE(sampler);
316 std::shared_ptr<RenderPass> r2t_pass;
317 auto cmd_buffer = context->CreateCommandBuffer();
318 ASSERT_TRUE(cmd_buffer);
319 {
320 ColorAttachment color0;
321 color0.load_action = LoadAction::kClear;
322 color0.store_action = StoreAction::kStore;
323
324 TextureDescriptor texture_descriptor;
325 ASSERT_NE(pipeline_desc->GetColorAttachmentDescriptor(0u), nullptr);
326 texture_descriptor.format =
327 pipeline_desc->GetColorAttachmentDescriptor(0u)->format;
328 texture_descriptor.storage_mode = StorageMode::kHostVisible;
329 texture_descriptor.size = {400, 400};
330 texture_descriptor.mip_count = 1u;
331 texture_descriptor.usage =
332 static_cast<TextureUsageMask>(TextureUsage::kRenderTarget);
333
334 color0.texture =
335 context->GetResourceAllocator()->CreateTexture(texture_descriptor);
336
337 ASSERT_TRUE(color0.IsValid());
338
339 color0.texture->SetLabel("r2t_target");
340
341 StencilAttachment stencil0;
342 stencil0.load_action = LoadAction::kClear;
343 stencil0.store_action = StoreAction::kDontCare;
344 TextureDescriptor stencil_texture_desc;
345 stencil_texture_desc.storage_mode = StorageMode::kDeviceTransient;
346 stencil_texture_desc.size = texture_descriptor.size;
347 stencil_texture_desc.format = PixelFormat::kS8UInt;
348 stencil_texture_desc.usage =
349 static_cast<TextureUsageMask>(TextureUsage::kRenderTarget);
350 stencil0.texture =
351 context->GetResourceAllocator()->CreateTexture(stencil_texture_desc);
352
353 RenderTarget r2t_desc;
354 r2t_desc.SetColorAttachment(color0, 0u);
355 r2t_desc.SetStencilAttachment(stencil0);
356 r2t_pass = cmd_buffer->CreateRenderPass(r2t_desc);
357 ASSERT_TRUE(r2t_pass && r2t_pass->IsValid());
358 }
359
360 Command cmd;
361 DEBUG_COMMAND_INFO(cmd, "Box");
362 cmd.pipeline = box_pipeline;
363
364 cmd.BindVertices(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(cmd,
373 r2t_pass->GetTransientsBuffer().EmplaceUniform(frame_info));
374 FS::BindContents1(cmd, boston, sampler);
375 FS::BindContents2(cmd, bridge, sampler);
376
377 VS::UniformBuffer uniforms;
378 uniforms.mvp = Matrix::MakeOrthographic(ISize{1024, 768}) *
379 Matrix::MakeTranslation({50.0f, 50.0f, 0.0f});
380 VS::BindUniformBuffer(
381 cmd, r2t_pass->GetTransientsBuffer().EmplaceUniform(uniforms));
382 ASSERT_TRUE(r2t_pass->AddCommand(std::move(cmd)));
383 ASSERT_TRUE(r2t_pass->EncodeCommands());
384 }
385
386 TEST_P(RendererTest, CanRenderInstanced) {
387 if (GetParam() == PlaygroundBackend::kOpenGLES) {
388 GTEST_SKIP_("Instancing is not supported on OpenGL.");
389 }
390 using VS = InstancedDrawVertexShader;
391 using FS = InstancedDrawFragmentShader;
392
393 VertexBufferBuilder<VS::PerVertexData> builder;
394
395 ASSERT_EQ(Tessellator::Result::kSuccess,
396 Tessellator{}.Tessellate(
397 PathBuilder{}
398 .AddRect(Rect::MakeXYWH(10, 10, 100, 100))
399 .TakePath(FillType::kPositive),
400 1.0f,
401 [&builder](const float* vertices, size_t vertices_count,
402 const uint16_t* indices, size_t indices_count) {
403 for (auto i = 0u; i < vertices_count * 2; i += 2) {
404 VS::PerVertexData data;
405 data.vtx = {vertices[i], vertices[i + 1]};
406 builder.AppendVertex(data);
407 }
408 for (auto i = 0u; i < indices_count; i++) {
409 builder.AppendIndex(indices[i]);
410 }
411 return true;
412 }));
413
414 ASSERT_NE(GetContext(), nullptr);
415 auto pipeline =
416 GetContext()
417 ->GetPipelineLibrary()
418 ->GetPipeline(PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(
419 *GetContext())
420 ->SetSampleCount(SampleCount::kCount4)
421 .SetStencilAttachmentDescriptors(std::nullopt))
422
423 .Get();
424 ASSERT_TRUE(pipeline && pipeline->IsValid());
425
426 Command cmd;
427 cmd.pipeline = pipeline;
428 DEBUG_COMMAND_INFO(cmd, "InstancedDraw");
429
430 static constexpr size_t kInstancesCount = 5u;
431 VS::InstanceInfo<kInstancesCount> instances;
432 for (size_t i = 0; i < kInstancesCount; i++) {
433 instances.colors[i] = Color::Random();
434 }
435
436 ASSERT_TRUE(OpenPlaygroundHere([&](RenderPass& pass) -> bool {
437 VS::FrameInfo frame_info;
438 EXPECT_EQ(pass.GetOrthographicTransform(),
439 Matrix::MakeOrthographic(pass.GetRenderTargetSize()));
440 frame_info.mvp =
441 pass.GetOrthographicTransform() * Matrix::MakeScale(GetContentScale());
442 VS::BindFrameInfo(cmd,
443 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
444 VS::BindInstanceInfo(
445 cmd, pass.GetTransientsBuffer().EmplaceStorageBuffer(instances));
446 cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
447
448 cmd.instance_count = kInstancesCount;
449 pass.AddCommand(std::move(cmd));
450 return true;
451 }));
452 }
453
454 TEST_P(RendererTest, CanBlitTextureToTexture) {
455 auto context = GetContext();
456 ASSERT_TRUE(context);
457
458 using VS = MipmapsVertexShader;
459 using FS = MipmapsFragmentShader;
460 auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
461 ASSERT_TRUE(desc.has_value());
462 desc->SetSampleCount(SampleCount::kCount4);
463 desc->SetStencilAttachmentDescriptors(std::nullopt);
464 auto mipmaps_pipeline =
465 context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
466 ASSERT_TRUE(mipmaps_pipeline);
467
468 TextureDescriptor texture_desc;
469 texture_desc.storage_mode = StorageMode::kHostVisible;
470 texture_desc.format = PixelFormat::kR8G8B8A8UNormInt;
471 texture_desc.size = {800, 600};
472 texture_desc.mip_count = 1u;
473 texture_desc.usage =
474 static_cast<TextureUsageMask>(TextureUsage::kRenderTarget) |
475 static_cast<TextureUsageMask>(TextureUsage::kShaderRead);
476 auto texture = context->GetResourceAllocator()->CreateTexture(texture_desc);
477 ASSERT_TRUE(texture);
478
479 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
480 auto boston = CreateTextureForFixture("boston.jpg");
481 ASSERT_TRUE(bridge && boston);
482 auto sampler = context->GetSamplerLibrary()->GetSampler({});
483 ASSERT_TRUE(sampler);
484
485 // Vertex buffer.
486 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
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 Renderer::RenderCallback callback = [&](RenderTarget& render_target) {
502 auto buffer = context->CreateCommandBuffer();
503 if (!buffer) {
504 return false;
505 }
506 buffer->SetLabel("Playground Command Buffer");
507
508 {
509 auto pass = buffer->CreateBlitPass();
510 if (!pass) {
511 return false;
512 }
513 pass->SetLabel("Playground Blit Pass");
514
515 if (render_target.GetColorAttachments().empty()) {
516 return false;
517 }
518
519 // Blit `bridge` to the top left corner of the texture.
520 pass->AddCopy(bridge, texture);
521
522 if (!pass->EncodeCommands(context->GetResourceAllocator())) {
523 return false;
524 }
525 }
526
527 {
528 auto pass = buffer->CreateRenderPass(render_target);
529 if (!pass) {
530 return false;
531 }
532 pass->SetLabel("Playground Render Pass");
533 {
534 Command cmd;
535 DEBUG_COMMAND_INFO(cmd, "Image");
536 cmd.pipeline = mipmaps_pipeline;
537
538 cmd.BindVertices(vertex_buffer);
539
540 VS::FrameInfo frame_info;
541 EXPECT_EQ(pass->GetOrthographicTransform(),
542 Matrix::MakeOrthographic(pass->GetRenderTargetSize()));
543 frame_info.mvp = pass->GetOrthographicTransform() *
544 Matrix::MakeScale(GetContentScale());
545 VS::BindFrameInfo(
546 cmd, pass->GetTransientsBuffer().EmplaceUniform(frame_info));
547
548 FS::FragInfo frag_info;
549 frag_info.lod = 0;
550 FS::BindFragInfo(cmd,
551 pass->GetTransientsBuffer().EmplaceUniform(frag_info));
552
553 auto sampler = context->GetSamplerLibrary()->GetSampler({});
554 FS::BindTex(cmd, texture, sampler);
555
556 pass->AddCommand(std::move(cmd));
557 }
558 pass->EncodeCommands();
559 }
560
561 if (!buffer->SubmitCommands()) {
562 return false;
563 }
564 return true;
565 };
566 OpenPlaygroundHere(callback);
567 }
568
569 TEST_P(RendererTest, CanBlitTextureToBuffer) {
570 auto context = GetContext();
571 ASSERT_TRUE(context);
572
573 using VS = MipmapsVertexShader;
574 using FS = MipmapsFragmentShader;
575 auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
576 ASSERT_TRUE(desc.has_value());
577 desc->SetSampleCount(SampleCount::kCount4);
578 desc->SetStencilAttachmentDescriptors(std::nullopt);
579 auto mipmaps_pipeline =
580 context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
581 ASSERT_TRUE(mipmaps_pipeline);
582
583 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
584 auto boston = CreateTextureForFixture("boston.jpg");
585 ASSERT_TRUE(bridge && boston);
586 auto sampler = context->GetSamplerLibrary()->GetSampler({});
587 ASSERT_TRUE(sampler);
588
589 TextureDescriptor texture_desc;
590 texture_desc.storage_mode = StorageMode::kHostVisible;
591 texture_desc.format = PixelFormat::kR8G8B8A8UNormInt;
592 texture_desc.size = bridge->GetTextureDescriptor().size;
593 texture_desc.mip_count = 1u;
594 texture_desc.usage =
595 static_cast<TextureUsageMask>(TextureUsage::kRenderTarget) |
596 static_cast<TextureUsageMask>(TextureUsage::kShaderWrite) |
597 static_cast<TextureUsageMask>(TextureUsage::kShaderRead);
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.
606 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
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 Renderer::RenderCallback callback = [&](RenderTarget& render_target) {
622 {
623 auto buffer = context->CreateCommandBuffer();
624 if (!buffer) {
625 return false;
626 }
627 buffer->SetLabel("Playground Command Buffer");
628 auto pass = buffer->CreateBlitPass();
629 if (!pass) {
630 return false;
631 }
632 pass->SetLabel("Playground Blit Pass");
633
634 if (render_target.GetColorAttachments().empty()) {
635 return false;
636 }
637
638 // Blit `bridge` to the top left corner of the texture.
639 pass->AddCopy(bridge, device_buffer);
640
641 pass->EncodeCommands(context->GetResourceAllocator());
642
643 if (!buffer->SubmitCommands()) {
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 Command cmd;
662 DEBUG_COMMAND_INFO(cmd, "Image");
663 cmd.pipeline = mipmaps_pipeline;
664
665 cmd.BindVertices(vertex_buffer);
666
667 VS::FrameInfo frame_info;
668 EXPECT_EQ(pass->GetOrthographicTransform(),
669 Matrix::MakeOrthographic(pass->GetRenderTargetSize()));
670 frame_info.mvp = pass->GetOrthographicTransform() *
671 Matrix::MakeScale(GetContentScale());
672 VS::BindFrameInfo(
673 cmd, pass->GetTransientsBuffer().EmplaceUniform(frame_info));
674
675 FS::FragInfo frag_info;
676 frag_info.lod = 0;
677 FS::BindFragInfo(cmd,
678 pass->GetTransientsBuffer().EmplaceUniform(frag_info));
679
680 auto sampler = context->GetSamplerLibrary()->GetSampler({});
681 auto buffer_view = device_buffer->AsBufferView();
682 auto texture =
683 context->GetResourceAllocator()->CreateTexture(texture_desc);
684 if (!texture->SetContents(buffer_view.contents,
685 buffer_view.range.length)) {
686 VALIDATION_LOG << "Could not upload texture to device memory";
687 return false;
688 }
689 FS::BindTex(cmd, texture, sampler);
690
691 pass->AddCommand(std::move(cmd));
692 }
693 pass->EncodeCommands();
694 if (!buffer->SubmitCommands()) {
695 return false;
696 }
697 }
698 return true;
699 };
700 OpenPlaygroundHere(callback);
701 }
702
703 TEST_P(RendererTest, CanGenerateMipmaps) {
704 auto context = GetContext();
705 ASSERT_TRUE(context);
706
707 using VS = MipmapsVertexShader;
708 using FS = MipmapsFragmentShader;
709 auto desc = PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
710 ASSERT_TRUE(desc.has_value());
711 desc->SetSampleCount(SampleCount::kCount4);
712 desc->SetStencilAttachmentDescriptors(std::nullopt);
713 auto mipmaps_pipeline =
714 context->GetPipelineLibrary()->GetPipeline(std::move(desc)).Get();
715 ASSERT_TRUE(mipmaps_pipeline);
716
717 auto boston = CreateTextureForFixture("boston.jpg", true);
718 ASSERT_TRUE(boston);
719
720 // Vertex buffer.
721 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
722 vertex_builder.SetLabel("Box");
723 auto size = Point(boston->GetSize());
724 vertex_builder.AddVertices({
725 {{0, 0}, {0.0, 0.0}}, // 1
726 {{size.x, 0}, {1.0, 0.0}}, // 2
727 {{size.x, size.y}, {1.0, 1.0}}, // 3
728 {{0, 0}, {0.0, 0.0}}, // 1
729 {{size.x, size.y}, {1.0, 1.0}}, // 3
730 {{0, size.y}, {0.0, 1.0}}, // 4
731 });
732 auto vertex_buffer =
733 vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
734 ASSERT_TRUE(vertex_buffer);
735
736 bool first_frame = true;
737 Renderer::RenderCallback callback = [&](RenderTarget& render_target) {
738 const char* mip_filter_names[] = {"Nearest", "Linear"};
739 const MipFilter mip_filters[] = {MipFilter::kNearest, MipFilter::kLinear};
740 const char* min_filter_names[] = {"Nearest", "Linear"};
741 const MinMagFilter min_filters[] = {MinMagFilter::kNearest,
742 MinMagFilter::kLinear};
743
744 // UI state.
745 static int selected_mip_filter = 1;
746 static int selected_min_filter = 0;
747 static float lod = 4.5;
748
749 ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_AlwaysAutoResize);
750 ImGui::Combo("Mip filter", &selected_mip_filter, mip_filter_names,
751 sizeof(mip_filter_names) / sizeof(char*));
752 ImGui::Combo("Min filter", &selected_min_filter, min_filter_names,
753 sizeof(min_filter_names) / sizeof(char*));
754 ImGui::SliderFloat("LOD", &lod, 0, boston->GetMipCount() - 1);
755 ImGui::End();
756
757 auto buffer = context->CreateCommandBuffer();
758 if (!buffer) {
759 return false;
760 }
761 buffer->SetLabel("Playground Command Buffer");
762
763 if (first_frame) {
764 auto pass = buffer->CreateBlitPass();
765 if (!pass) {
766 return false;
767 }
768 pass->SetLabel("Playground Blit Pass");
769
770 pass->GenerateMipmap(boston, "Boston Mipmap");
771
772 pass->EncodeCommands(context->GetResourceAllocator());
773 }
774
775 first_frame = false;
776
777 {
778 auto pass = buffer->CreateRenderPass(render_target);
779 if (!pass) {
780 return false;
781 }
782 pass->SetLabel("Playground Render Pass");
783 {
784 Command cmd;
785 DEBUG_COMMAND_INFO(cmd, "Image LOD");
786 cmd.pipeline = mipmaps_pipeline;
787
788 cmd.BindVertices(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(
796 cmd, pass->GetTransientsBuffer().EmplaceUniform(frame_info));
797
798 FS::FragInfo frag_info;
799 frag_info.lod = lod;
800 FS::BindFragInfo(cmd,
801 pass->GetTransientsBuffer().EmplaceUniform(frag_info));
802
803 SamplerDescriptor sampler_desc;
804 sampler_desc.mip_filter = mip_filters[selected_mip_filter];
805 sampler_desc.min_filter = min_filters[selected_min_filter];
806 auto sampler = context->GetSamplerLibrary()->GetSampler(sampler_desc);
807 FS::BindTex(cmd, boston, sampler);
808
809 pass->AddCommand(std::move(cmd));
810 }
811 pass->EncodeCommands();
812 }
813
814 if (!buffer->SubmitCommands()) {
815 return false;
816 }
817 return true;
818 };
819 OpenPlaygroundHere(callback);
820 }
821
822 TEST_P(RendererTest, TheImpeller) {
823 using VS = ImpellerVertexShader;
824 using FS = ImpellerFragmentShader;
825
826 auto context = GetContext();
827 auto pipeline_descriptor =
828 PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
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;
838 noise_sampler_desc.width_address_mode = SamplerAddressMode::kRepeat;
839 noise_sampler_desc.height_address_mode = SamplerAddressMode::kRepeat;
840 auto 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 auto cube_map_sampler = context->GetSamplerLibrary()->GetSampler({});
848
849 SinglePassCallback callback = [&](RenderPass& pass) {
850 auto size = pass.GetRenderTargetSize();
851
852 Command cmd;
853 cmd.pipeline = pipeline;
854 DEBUG_COMMAND_INFO(cmd, "Impeller SDF scene");
855 VertexBufferBuilder<VS::PerVertexData> builder;
856 builder.AddVertices({{Point()},
857 {Point(0, size.height)},
858 {Point(size.width, 0)},
859 {Point(size.width, 0)},
860 {Point(0, size.height)},
861 {Point(size.width, size.height)}});
862 cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
863
864 VS::FrameInfo frame_info;
865 EXPECT_EQ(pass.GetOrthographicTransform(), Matrix::MakeOrthographic(size));
866 frame_info.mvp = pass.GetOrthographicTransform();
867 VS::BindFrameInfo(cmd,
868 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
869
870 FS::FragInfo fs_uniform;
871 fs_uniform.texture_size = Point(size);
872 fs_uniform.time = GetSecondsElapsed();
873 FS::BindFragInfo(cmd,
874 pass.GetTransientsBuffer().EmplaceUniform(fs_uniform));
875 FS::BindBlueNoise(cmd, blue_noise, noise_sampler);
876 FS::BindCubeMap(cmd, cube_map, cube_map_sampler);
877
878 pass.AddCommand(std::move(cmd));
879 return true;
880 };
881 OpenPlaygroundHere(callback);
882 }
883
884 TEST_P(RendererTest, ArrayUniforms) {
885 using VS = ArrayVertexShader;
886 using FS = ArrayFragmentShader;
887
888 auto context = GetContext();
889 auto pipeline_descriptor =
890 PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
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 SinglePassCallback callback = [&](RenderPass& pass) {
899 auto size = pass.GetRenderTargetSize();
900
901 Command cmd;
902 cmd.pipeline = pipeline;
903 DEBUG_COMMAND_INFO(cmd, "Google Dots");
904 VertexBufferBuilder<VS::PerVertexData> builder;
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 cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
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(cmd,
918 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
919
920 auto time = GetSecondsElapsed();
921 auto y_pos = [&time](float x) {
922 return 400 + 10 * std::cos(time * 5 + x / 6);
923 };
924
925 FS::FragInfo fs_uniform = {
926 .circle_positions = {Point(430, y_pos(0)), Point(480, y_pos(1)),
927 Point(530, y_pos(2)), Point(580, y_pos(3))},
928 .colors = {Color::MakeRGBA8(66, 133, 244, 255),
929 Color::MakeRGBA8(219, 68, 55, 255),
930 Color::MakeRGBA8(244, 180, 0, 255),
931 Color::MakeRGBA8(15, 157, 88, 255)},
932 };
933 FS::BindFragInfo(cmd,
934 pass.GetTransientsBuffer().EmplaceUniform(fs_uniform));
935
936 pass.AddCommand(std::move(cmd));
937 return true;
938 };
939 OpenPlaygroundHere(callback);
940 }
941
942 TEST_P(RendererTest, InactiveUniforms) {
943 using VS = InactiveUniformsVertexShader;
944 using FS = InactiveUniformsFragmentShader;
945
946 auto context = GetContext();
947 auto pipeline_descriptor =
948 PipelineBuilder<VS, FS>::MakeDefaultPipelineDescriptor(*context);
949 ASSERT_TRUE(pipeline_descriptor.has_value());
950 pipeline_descriptor->SetSampleCount(SampleCount::kCount4);
951 pipeline_descriptor->SetStencilAttachmentDescriptors(std::nullopt);
952 auto pipeline =
953 context->GetPipelineLibrary()->GetPipeline(pipeline_descriptor).Get();
954 ASSERT_TRUE(pipeline && pipeline->IsValid());
955
956 SinglePassCallback callback = [&](RenderPass& pass) {
957 auto size = pass.GetRenderTargetSize();
958
959 Command cmd;
960 cmd.pipeline = pipeline;
961 DEBUG_COMMAND_INFO(cmd, "Inactive Uniform");
962 VertexBufferBuilder<VS::PerVertexData> builder;
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 cmd.BindVertices(builder.CreateVertexBuffer(pass.GetTransientsBuffer()));
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(cmd,
976 pass.GetTransientsBuffer().EmplaceUniform(frame_info));
977
978 FS::FragInfo fs_uniform = {.unused_color = Color::Red(),
979 .color = Color::Green()};
980 FS::BindFragInfo(cmd,
981 pass.GetTransientsBuffer().EmplaceUniform(fs_uniform));
982
983 pass.AddCommand(std::move(cmd));
984 return true;
985 };
986 OpenPlaygroundHere(callback);
987 }
988
989 TEST_P(RendererTest, CanCreateCPUBackedTexture) {
990 if (GetParam() == PlaygroundBackend::kOpenGLES) {
991 GTEST_SKIP_("CPU backed textures are not supported on OpenGLES.");
992 }
993
994 auto context = GetContext();
995 auto allocator = context->GetResourceAllocator();
996 size_t dimension = 2;
997
998 do {
999 ISize size(dimension, dimension);
1000 TextureDescriptor texture_descriptor;
1001 texture_descriptor.storage_mode = StorageMode::kHostVisible;
1002 texture_descriptor.format = PixelFormat::kR8G8B8A8UNormInt;
1003 texture_descriptor.size = size;
1004 auto row_bytes =
1005 std::max(static_cast<uint16_t>(size.width * 4),
1006 allocator->MinimumBytesPerRow(texture_descriptor.format));
1007 auto buffer_size = size.height * row_bytes;
1008
1009 DeviceBufferDescriptor buffer_descriptor;
1010 buffer_descriptor.storage_mode = StorageMode::kHostVisible;
1011 buffer_descriptor.size = buffer_size;
1012
1013 auto buffer = allocator->CreateBuffer(buffer_descriptor);
1014
1015 ASSERT_TRUE(buffer);
1016
1017 auto texture = buffer->AsTexture(*allocator, texture_descriptor, row_bytes);
1018
1019 ASSERT_TRUE(texture);
1020 ASSERT_TRUE(texture->IsValid());
1021
1022 dimension *= 2;
1023 } while (dimension <= 8192);
1024 }
1025
1026 TEST_P(RendererTest, DefaultIndexSize) {
1027 using VS = BoxFadeVertexShader;
1028
1029 // Default to 16bit index buffer size, as this is a reasonable default and
1030 // supported on all backends without extensions.
1031 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
1032 vertex_builder.AppendIndex(0u);
1033 ASSERT_EQ(vertex_builder.GetIndexType(), IndexType::k16bit);
1034 }
1035
1036 TEST_P(RendererTest, DefaultIndexBehavior) {
1037 using VS = BoxFadeVertexShader;
1038
1039 // Do not create any index buffer if no indices were provided.
1040 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
1041 ASSERT_EQ(vertex_builder.GetIndexType(), IndexType::kNone);
1042 }
1043
1044 TEST_P(RendererTest, VertexBufferBuilder) {
1045 // Does not create index buffer if one is provided.
1046 using VS = BoxFadeVertexShader;
1047 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
1048 vertex_builder.SetLabel("Box");
1049 vertex_builder.AddVertices({
1050 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1051 {{800, 100, 0.0}, {1.0, 0.0}}, // 2
1052 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1053 {{100, 800, 0.0}, {0.0, 1.0}}, // 4
1054 });
1055 vertex_builder.AppendIndex(0);
1056 vertex_builder.AppendIndex(1);
1057 vertex_builder.AppendIndex(2);
1058 vertex_builder.AppendIndex(1);
1059 vertex_builder.AppendIndex(2);
1060 vertex_builder.AppendIndex(3);
1061
1062 ASSERT_EQ(vertex_builder.GetIndexCount(), 6u);
1063 ASSERT_EQ(vertex_builder.GetVertexCount(), 4u);
1064 }
1065
1066 class CompareFunctionUIData {
1067 public:
1068 CompareFunctionUIData() {
1069 labels_.push_back("Never");
1070 functions_.push_back(CompareFunction::kNever);
1071 labels_.push_back("Always");
1072 functions_.push_back(CompareFunction::kAlways);
1073 labels_.push_back("Less");
1074 functions_.push_back(CompareFunction::kLess);
1075 labels_.push_back("Equal");
1076 functions_.push_back(CompareFunction::kEqual);
1077 labels_.push_back("LessEqual");
1078 functions_.push_back(CompareFunction::kLessEqual);
1079 labels_.push_back("Greater");
1080 functions_.push_back(CompareFunction::kGreater);
1081 labels_.push_back("NotEqual");
1082 functions_.push_back(CompareFunction::kNotEqual);
1083 labels_.push_back("GreaterEqual");
1084 functions_.push_back(CompareFunction::kGreaterEqual);
1085 assert(labels_.size() == functions_.size());
1086 }
1087
1088 const char* const* labels() const { return &labels_[0]; }
1089
1090 int size() const { return labels_.size(); }
1091
1092 int IndexOf(CompareFunction func) const {
1093 for (size_t i = 0; i < functions_.size(); i++) {
1094 if (functions_[i] == func) {
1095 return i;
1096 }
1097 }
1098 FML_UNREACHABLE();
1099 return -1;
1100 }
1101
1102 CompareFunction FunctionOf(int index) const { return functions_[index]; }
1103
1104 private:
1105 std::vector<const char*> labels_;
1106 std::vector<CompareFunction> functions_;
1107 };
1108
1109 static const CompareFunctionUIData& CompareFunctionUI() {
1110 static CompareFunctionUIData data;
1111 return data;
1112 }
1113
1114 TEST_P(RendererTest, StencilMask) {
1115 using VS = BoxFadeVertexShader;
1116 using FS = BoxFadeFragmentShader;
1117 auto context = GetContext();
1118 ASSERT_TRUE(context);
1119 using BoxFadePipelineBuilder = PipelineBuilder<VS, FS>;
1120 auto desc = BoxFadePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
1121 ASSERT_TRUE(desc.has_value());
1122
1123 // Vertex buffer.
1124 VertexBufferBuilder<VS::PerVertexData> vertex_builder;
1125 vertex_builder.SetLabel("Box");
1126 vertex_builder.AddVertices({
1127 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1128 {{800, 100, 0.0}, {1.0, 0.0}}, // 2
1129 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1130 {{100, 100, 0.0}, {0.0, 0.0}}, // 1
1131 {{800, 800, 0.0}, {1.0, 1.0}}, // 3
1132 {{100, 800, 0.0}, {0.0, 1.0}}, // 4
1133 });
1134 auto vertex_buffer =
1135 vertex_builder.CreateVertexBuffer(*context->GetResourceAllocator());
1136 ASSERT_TRUE(vertex_buffer);
1137
1138 desc->SetSampleCount(SampleCount::kCount4);
1139 desc->SetStencilAttachmentDescriptors(std::nullopt);
1140
1141 auto bridge = CreateTextureForFixture("bay_bridge.jpg");
1142 auto boston = CreateTextureForFixture("boston.jpg");
1143 ASSERT_TRUE(bridge && boston);
1144 auto sampler = 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 =
1153 CompareFunctionUI().IndexOf(CompareFunction::kLessEqual);
1154 static int current_back_compare =
1155 CompareFunctionUI().IndexOf(CompareFunction::kLessEqual);
1156 Renderer::RenderCallback callback = [&](RenderTarget& render_target) {
1157 auto buffer = context->CreateCommandBuffer();
1158 if (!buffer) {
1159 return false;
1160 }
1161 buffer->SetLabel("Playground Command Buffer");
1162
1163 {
1164 // Configure the stencil attachment for the test.
1165 RenderTarget::AttachmentConfig stencil_config;
1166 stencil_config.load_action = LoadAction::kLoad;
1167 stencil_config.store_action = StoreAction::kDontCare;
1168 stencil_config.storage_mode = StorageMode::kHostVisible;
1169 auto render_target_allocator =
1170 RenderTargetAllocator(context->GetResourceAllocator());
1171 render_target.SetupStencilAttachment(*context, render_target_allocator,
1172 render_target.GetRenderTargetSize(),
1173 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
1215 StencilAttachmentDescriptor front;
1216 front.stencil_compare =
1217 CompareFunctionUI().FunctionOf(current_front_compare);
1218 StencilAttachmentDescriptor back;
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 Command cmd;
1227 DEBUG_COMMAND_INFO(cmd, "Box");
1228 cmd.pipeline = pipeline;
1229 cmd.stencil_reference = stencil_reference_read;
1230
1231 cmd.BindVertices(vertex_buffer);
1232
1233 VS::UniformBuffer uniforms;
1234 EXPECT_EQ(pass->GetOrthographicTransform(),
1235 Matrix::MakeOrthographic(pass->GetRenderTargetSize()));
1236 uniforms.mvp = pass->GetOrthographicTransform() *
1237 Matrix::MakeScale(GetContentScale());
1238 if (mirror) {
1239 uniforms.mvp = Matrix::MakeScale(Vector2(-1, 1)) * uniforms.mvp;
1240 }
1241 VS::BindUniformBuffer(
1242 cmd, pass->GetTransientsBuffer().EmplaceUniform(uniforms));
1243
1244 FS::FrameInfo frame_info;
1245 frame_info.current_time = GetSecondsElapsed();
1246 frame_info.cursor_position = GetCursorPosition();
1247 frame_info.window_size.x = GetWindowSize().width;
1248 frame_info.window_size.y = GetWindowSize().height;
1249
1250 FS::BindFrameInfo(cmd,
1251 pass->GetTransientsBuffer().EmplaceUniform(frame_info));
1252 FS::BindContents1(cmd, boston, sampler);
1253 FS::BindContents2(cmd, bridge, sampler);
1254 if (!pass->AddCommand(std::move(cmd))) {
1255 return false;
1256 }
1257 pass->EncodeCommands();
1258 }
1259
1260 if (!buffer->SubmitCommands()) {
1261 return false;
1262 }
1263 return true;
1264 };
1265 OpenPlaygroundHere(callback);
1266 }
1267
1268 TEST_P(RendererTest, CanPreAllocateCommands) {
1269 auto context = GetContext();
1270 auto cmd_buffer = context->CreateCommandBuffer();
1271 auto render_target_cache = std::make_shared<RenderTargetAllocator>(
1272 GetContext()->GetResourceAllocator());
1273
1274 auto render_target =
1275 RenderTarget::CreateOffscreen(*context, *render_target_cache, {100, 100});
1276 auto render_pass = cmd_buffer->CreateRenderPass(render_target);
1277
1278 render_pass->ReserveCommands(100u);
1279
1280 EXPECT_EQ(render_pass->GetCommands().capacity(), 100u);
1281 }
1282
1283 TEST_P(RendererTest, CanLookupRenderTargetProperties) {
1284 auto context = GetContext();
1285 auto cmd_buffer = context->CreateCommandBuffer();
1286 auto render_target_cache = std::make_shared<RenderTargetAllocator>(
1287 GetContext()->GetResourceAllocator());
1288
1289 auto render_target =
1290 RenderTarget::CreateOffscreen(*context, *render_target_cache, {100, 100});
1291 auto render_pass = cmd_buffer->CreateRenderPass(render_target);
1292
1293 EXPECT_EQ(render_pass->GetSampleCount(), render_target.GetSampleCount());
1294 EXPECT_EQ(render_pass->GetRenderTargetPixelFormat(),
1295 render_target.GetRenderTargetPixelFormat());
1296 EXPECT_EQ(render_pass->HasStencilAttachment(),
1297 render_target.GetStencilAttachment().has_value());
1298 EXPECT_EQ(render_pass->GetRenderTargetSize(),
1299 render_target.GetRenderTargetSize());
1300 }
1301
1302 } // namespace testing
1303 } // namespace impeller
1304
1305 // NOLINTEND(bugprone-unchecked-optional-access)
impeller::Color::Blue
static constexpr Color Blue()
Definition: color.h:268
impeller::PixelFormat::kS8UInt
@ kS8UInt
impeller::Command
An object used to specify work to the GPU along with references to resources the GPU will used when d...
Definition: command.h:92
impeller::VertexBuffer::index_type
IndexType index_type
Definition: vertex_buffer.h:29
DEBUG_COMMAND_INFO
#define DEBUG_COMMAND_INFO(obj, arg)
Definition: command.h:28
impeller::Matrix::MakeRotationX
static Matrix MakeRotationX(Radians r)
Definition: matrix.h:182
impeller::RenderTarget::AttachmentConfig::store_action
StoreAction store_action
Definition: render_target.h:54
impeller::CompareFunction::kGreater
@ kGreater
Comparison test passes if new_value > current_value.
impeller::testing::CompareFunctionUI
static const CompareFunctionUIData & CompareFunctionUI()
Definition: renderer_unittests.cc:1109
impeller::LoadAction::kLoad
@ kLoad
impeller::RenderTarget::AttachmentConfig::load_action
LoadAction load_action
Definition: render_target.h:53
impeller::TextureUsageMask
uint64_t TextureUsageMask
Definition: formats.h:295
impeller::IndexType::k16bit
@ k16bit
impeller::Attachment::store_action
StoreAction store_action
Definition: formats.h:637
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::StencilAttachmentDescriptor::stencil_compare
CompareFunction stencil_compare
Definition: formats.h:593
impeller::testing::CompareFunctionUIData::IndexOf
int IndexOf(CompareFunction func) const
Definition: renderer_unittests.cc:1092
impeller::Degrees::degrees
Scalar degrees
Definition: scalar.h:47
impeller::Color::Red
static constexpr Color Red()
Definition: color.h:264
impeller::TRect< Scalar >::MakeXYWH
constexpr static TRect MakeXYWH(Type x, Type y, Type width, Type height)
Definition: rect.h:34
impeller::Tessellator
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition: tessellator.h:39
impeller::Color::MakeRGBA8
static constexpr Color MakeRGBA8(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
Definition: color.h:154
impeller::testing::CompareFunctionUIData
Definition: renderer_unittests.cc:1066
impeller::DeviceBufferDescriptor
Definition: device_buffer_descriptor.h:14
impeller::Renderer::RenderCallback
std::function< bool(RenderTarget &render_target)> RenderCallback
Definition: renderer.h:23
impeller::VertexBufferBuilder::GetVertexCount
size_t GetVertexCount() const
Definition: vertex_buffer_builder.h:54
impeller::CompareFunction::kEqual
@ kEqual
Comparison test passes if new_value == current_value.
impeller::ColorAttachment
Definition: formats.h:642
impeller::CompareFunction::kGreaterEqual
@ kGreaterEqual
Comparison test passes if new_value >= current_value.
impeller::TextureDescriptor::format
PixelFormat format
Definition: texture_descriptor.h:40
impeller::RenderTarget::SetColorAttachment
RenderTarget & SetColorAttachment(const ColorAttachment &attachment, size_t index)
Definition: render_target.cc:180
impeller::VertexBuffer
Definition: vertex_buffer.h:13
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
formats.h
impeller::PathBuilder
Definition: path_builder.h:14
impeller::TextureDescriptor::mip_count
size_t mip_count
Definition: texture_descriptor.h:42
impeller::Vector2
Point Vector2
Definition: point.h:312
impeller::Matrix::MakeRotationY
static Matrix MakeRotationY(Radians r)
Definition: matrix.h:195
impeller::TextureUsage::kRenderTarget
@ kRenderTarget
impeller::StoreAction::kDontCare
@ kDontCare
impeller::DeviceBufferDescriptor::size
size_t size
Definition: device_buffer_descriptor.h:16
impeller::VertexBufferBuilder::AddVertices
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
Definition: vertex_buffer_builder.h:70
impeller::Color::Yellow
static constexpr Color Yellow()
Definition: color.h:834
impeller::VertexBuffer::vertex_buffer
BufferView vertex_buffer
Definition: vertex_buffer.h:14
impeller::StorageMode::kHostVisible
@ kHostVisible
pipeline_builder.h
impeller::VertexBuffer::vertex_count
size_t vertex_count
Definition: vertex_buffer.h:23
impeller::Matrix::MakeTranslation
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
impeller::Command::instance_count
size_t instance_count
Definition: command.h:147
impeller::RenderPass::GetOrthographicTransform
const Matrix & GetOrthographicTransform() const
Definition: render_pass.cc:51
impeller::TextureDescriptor::usage
TextureUsageMask usage
Definition: texture_descriptor.h:43
impeller::Vector3::x
Scalar x
Definition: vector.h:23
impeller::PolygonMode::kFill
@ kFill
impeller::PathBuilder::AddRect
PathBuilder & AddRect(Rect rect)
Definition: path_builder.cc:116
impeller::FillType::kPositive
@ kPositive
impeller::testing::CompareFunctionUIData::CompareFunctionUIData
CompareFunctionUIData()
Definition: renderer_unittests.cc:1068
impeller::Matrix::MakePerspective
static constexpr Matrix MakePerspective(Radians fov_y, Scalar aspect_ratio, Scalar z_near, Scalar z_far)
Definition: matrix.h:468
impeller::RenderTarget::AttachmentConfig
Definition: render_target.h:51
tessellator.h
path_builder.h
impeller::MinMagFilter::kNearest
@ kNearest
Select nearest to the sample point. Most widely supported.
impeller::SamplerDescriptor
Definition: sampler_descriptor.h:15
impeller::testing::INSTANTIATE_PLAYGROUND_SUITE
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
command.h
impeller::StencilAttachment
Definition: formats.h:650
impeller::MipFilter::kNearest
@ kNearest
Sample from the nearest mip level.
impeller::RenderPass::GetRenderTargetSize
ISize GetRenderTargetSize() const
Definition: render_pass.cc:47
impeller::TSize< int64_t >
impeller::LoadAction::kClear
@ kClear
impeller::CompareFunction
CompareFunction
Definition: formats.h:529
impeller::SamplerDescriptor::min_filter
MinMagFilter min_filter
Definition: sampler_descriptor.h:16
impeller::StorageMode::kDeviceTransient
@ kDeviceTransient
impeller::Point
TPoint< Scalar > Point
Definition: point.h:308
impeller::CullMode::kBackFace
@ kBackFace
render_pass.h
impeller::testing::CompareFunctionUIData::FunctionOf
CompareFunction FunctionOf(int index) const
Definition: renderer_unittests.cc:1102
impeller::MipFilter
MipFilter
Definition: formats.h:412
impeller::Attachment::texture
std::shared_ptr< Texture > texture
Definition: formats.h:634
impeller::VertexBufferBuilder
Definition: vertex_buffer_builder.h:24
impeller::MinMagFilter::kLinear
@ kLinear
impeller::BufferView::buffer
std::shared_ptr< const Buffer > buffer
Definition: buffer_view.h:14
impeller::DeviceBufferDescriptor::storage_mode
StorageMode storage_mode
Definition: device_buffer_descriptor.h:15
impeller::Color::Random
static Color Random()
Definition: color.h:842
impeller::Command::BindVertices
bool BindVertices(VertexBuffer buffer)
Specify the vertex and index buffer to use for this command.
Definition: command.cc:15
impeller::SamplerDescriptor::width_address_mode
SamplerAddressMode width_address_mode
Definition: sampler_descriptor.h:20
impeller::TextureUsage::kShaderRead
@ kShaderRead
impeller::WindingOrder::kCounterClockwise
@ kCounterClockwise
impeller::Vector3::z
Scalar z
Definition: vector.h:25
impeller::VertexBufferBuilder::GetIndexCount
size_t GetIndexCount() const
Definition: vertex_buffer_builder.h:56
impeller::Radians
Definition: scalar.h:38
impeller::IndexType::kNone
@ kNone
Does not use the index buffer.
impeller::Tessellator::Result::kSuccess
@ kSuccess
impeller::RenderTarget::AttachmentConfig::storage_mode
StorageMode storage_mode
Definition: render_target.h:52
impeller::MinMagFilter
MinMagFilter
Definition: formats.h:404
impeller::testing::CompareFunctionUIData::size
int size() const
Definition: renderer_unittests.cc:1090
impeller::RenderTarget
Definition: render_target.h:49
impeller::Color::Green
static constexpr Color Green()
Definition: color.h:266
impeller::StoreAction::kStore
@ kStore
impeller::CompareFunction::kLessEqual
@ kLessEqual
Comparison test passes if new_value <= current_value.
impeller::Vector3::y
Scalar y
Definition: vector.h:24
impeller::CompareFunction::kAlways
@ kAlways
Comparison test passes always passes.
impeller::RenderTargetAllocator
a wrapper around the impeller [Allocator] instance that can be used to provide caching of allocated r...
Definition: render_target.h:23
impeller::VertexBufferBuilder::AppendIndex
VertexBufferBuilder & AppendIndex(IndexType_ index)
Definition: vertex_buffer_builder.h:79
impeller::VertexBufferBuilder::SetLabel
void SetLabel(const std::string &label)
Definition: vertex_buffer_builder.h:46
impeller::TSize::width
Type width
Definition: size.h:22
impeller::SamplerDescriptor::mip_filter
MipFilter mip_filter
Definition: sampler_descriptor.h:18
impeller::VertexBufferBuilder::CreateVertexBuffer
VertexBuffer CreateVertexBuffer(HostBuffer &host_buffer) const
Definition: vertex_buffer_builder.h:84
pipeline_library.h
impeller::CompareFunction::kNever
@ kNever
Comparison test never passes.
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:29
impeller::TextureDescriptor::size
ISize size
Definition: texture_descriptor.h:41
impeller::RenderTarget::CreateOffscreen
static RenderTarget CreateOffscreen(const Context &context, RenderTargetAllocator &allocator, ISize size, const std::string &label="Offscreen", AttachmentConfig color_attachment_config=kDefaultColorAttachmentConfig, std::optional< AttachmentConfig > stencil_attachment_config=kDefaultStencilAttachmentConfig)
Definition: render_target.cc:223
sampler_descriptor.h
impeller::VertexBuffer::index_buffer
BufferView index_buffer
The index buffer binding used by the vertex shader stage.
Definition: vertex_buffer.h:18
VALIDATION_LOG
#define VALIDATION_LOG
Definition: validation.h:67
command_buffer.h
impeller::Command::stencil_reference
uint32_t stencil_reference
Definition: command.h:122
impeller::VertexBufferBuilder::GetIndexType
constexpr impeller::IndexType GetIndexType() const
Definition: vertex_buffer_builder.h:33
impeller::PlaygroundBackend::kOpenGLES
@ kOpenGLES
impeller::Matrix::MakeRotationZ
static Matrix MakeRotationZ(Radians r)
Definition: matrix.h:209
impeller::Range
Definition: range.h:14
impeller::RenderTarget::SetStencilAttachment
RenderTarget & SetStencilAttachment(std::optional< StencilAttachment > attachment)
Definition: render_target.cc:199
impeller::Tessellator::Tessellate
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.
Definition: tessellator.cc:64
impeller::StencilAttachmentDescriptor
Definition: formats.h:587
impeller::Attachment::load_action
LoadAction load_action
Definition: formats.h:636
impeller::MipFilter::kLinear
@ kLinear
impeller::HostBuffer::EmplaceUniform
BufferView EmplaceUniform(const UniformType &uniform)
Emplace uniform data onto the host buffer. Ensure that backend specific uniform alignment requirement...
Definition: host_buffer.h:41
impeller::Matrix::MakeOrthographic
static constexpr Matrix MakeOrthographic(TSize< T > size)
Definition: matrix.h:459
impeller::VertexBufferBuilder::AppendVertex
VertexBufferBuilder & AppendVertex(VertexType_ vertex)
Definition: vertex_buffer_builder.h:65
impeller::Attachment::IsValid
bool IsValid() const
Definition: formats.cc:26
impeller::Degrees
Definition: scalar.h:46
impeller::SampleCount::kCount1
@ kCount1
impeller::SampleCount::kCount4
@ kCount4
impeller::PolygonMode::kLine
@ kLine
impeller::TextureDescriptor::storage_mode
StorageMode storage_mode
Definition: texture_descriptor.h:38
impeller::TSize::height
Type height
Definition: size.h:23
impeller::HostBuffer::EmplaceStorageBuffer
BufferView EmplaceStorageBuffer(const StorageBufferType &buffer)
Emplace storage buffer data onto the host buffer. Ensure that backend specific uniform alignment requ...
Definition: host_buffer.h:63
impeller::PlaygroundTest
Definition: playground_test.h:23
device_buffer_descriptor.h
impeller::TextureDescriptor
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
Definition: texture_descriptor.h:37
impeller::CompareFunction::kNotEqual
@ kNotEqual
Comparison test passes if new_value != current_value.
impeller::Command::pipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > pipeline
Definition: command.h:96
render_target.h
impeller::PipelineBuilder
An optional (but highly recommended) utility for creating pipelines from reflected shader information...
Definition: pipeline_builder.h:32
impeller::testing::TEST_P
TEST_P(AiksTest, CanRenderLinearGradientClamp)
Definition: aiks_gradient_unittests.cc:48
impeller::PipelineBuilder::MakeDefaultPipelineDescriptor
static std::optional< PipelineDescriptor > MakeDefaultPipelineDescriptor(const Context &context, const std::vector< Scalar > &constants={})
Create a default pipeline descriptor using the combination reflected shader information....
Definition: pipeline_builder.h:51
renderer.h
impeller::SamplerDescriptor::height_address_mode
SamplerAddressMode height_address_mode
Definition: sampler_descriptor.h:21
impeller::TextureUsage::kShaderWrite
@ kShaderWrite
impeller::RenderPass::AddCommand
bool AddCommand(Command &&command)
Record a command for subsequent encoding to the underlying command buffer. No work is encoded into th...
Definition: render_pass.cc:67
impeller::CompareFunction::kLess
@ kLess
Comparison test passes if new_value < current_value.
impeller
Definition: aiks_context.cc:10
playground_test.h
impeller::Matrix::MakeScale
static constexpr Matrix MakeScale(const Vector3 &s)
Definition: matrix.h:104
impeller::testing::CompareFunctionUIData::labels
const char *const * labels() const
Definition: renderer_unittests.cc:1088
impeller::RenderPass::GetTransientsBuffer
HostBuffer & GetTransientsBuffer()
Definition: render_pass.cc:55
impeller::Vector3
Definition: vector.h:20
vertex_buffer_builder.h
impeller::SamplerAddressMode::kRepeat
@ kRepeat