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