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