| Flutter Impeller |

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