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