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