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