Flutter Impeller
runtime_stage_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 <cstddef>
6 #include <future>
7 
8 #include "flutter/fml/make_copyable.h"
9 #include "flutter/testing/testing.h"
10 #include "gmock/gmock.h"
11 #include "gtest/gtest.h"
16 #include "impeller/entity/runtime_effect.vert.h"
22 #include "impeller/runtime_stage/runtime_stage_flatbuffers.h"
24 #include "runtime_stage_types_flatbuffers.h"
25 #include "third_party/abseil-cpp/absl/status/status_matchers.h"
26 
27 namespace impeller {
28 namespace testing {
29 
32 
33 TEST_P(RuntimeStageTest, CanReadValidBlob) {
34  const std::shared_ptr<fml::Mapping> fixture =
35  flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
36  ASSERT_TRUE(fixture);
37  ASSERT_GT(fixture->GetSize(), 0u);
38  auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
39  ABSL_ASSERT_OK(stages);
40  auto stage =
41  stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
42  ASSERT_TRUE(stage);
43  ASSERT_EQ(stage->GetShaderStage(), RuntimeShaderStage::kFragment);
44 }
45 
46 TEST_P(RuntimeStageTest, RejectInvalidFormatVersion) {
47  flatbuffers::FlatBufferBuilder builder;
48  fb::RuntimeStagesBuilder stages_builder(builder);
49  stages_builder.add_format_version(0);
50  auto stages = stages_builder.Finish();
51  builder.Finish(stages, fb::RuntimeStagesIdentifier());
52  auto mapping = std::make_shared<fml::NonOwnedMapping>(
53  builder.GetBufferPointer(), builder.GetSize());
54  auto runtime_stages = RuntimeStage::DecodeRuntimeStages(mapping);
55  EXPECT_FALSE(runtime_stages.ok());
56  EXPECT_EQ(runtime_stages.status().code(), absl::StatusCode::kInvalidArgument);
57 }
58 
59 TEST_P(RuntimeStageTest, CanRejectInvalidBlob) {
60  ScopedValidationDisable disable_validation;
61  const std::shared_ptr<fml::Mapping> fixture =
62  flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
63  ASSERT_TRUE(fixture);
64  auto junk_allocation = std::make_shared<Allocation>();
65  ASSERT_TRUE(junk_allocation->Truncate(Bytes{fixture->GetSize()}, false));
66  // Not meant to be secure. Just reject obviously bad blobs using magic
67  // numbers.
68  ::memset(junk_allocation->GetBuffer(), 127,
69  junk_allocation->GetLength().GetByteSize());
71  CreateMappingFromAllocation(junk_allocation));
72  ASSERT_FALSE(stages.ok());
73 }
74 
75 TEST_P(RuntimeStageTest, CanReadUniforms) {
76  const std::shared_ptr<fml::Mapping> fixture =
77  flutter::testing::OpenFixtureAsMapping(
78  "all_supported_uniforms.frag.iplr");
79  ASSERT_TRUE(fixture);
80  ASSERT_GT(fixture->GetSize(), 0u);
81  auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
82  ABSL_ASSERT_OK(stages);
83  auto stage =
84  stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
85 
86  ASSERT_TRUE(stage);
87  switch (GetBackend()) {
89  [[fallthrough]];
91  ASSERT_EQ(stage->GetUniforms().size(), 14u);
92  {
93  // uFloat
94  auto uni = stage->GetUniform("uFloat");
95  ASSERT_NE(uni, nullptr);
96  EXPECT_EQ(uni->dimensions.rows, 1u);
97  EXPECT_EQ(uni->dimensions.cols, 1u);
98  EXPECT_EQ(uni->location, 0u);
99  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
100  EXPECT_TRUE(uni->padding_layout.empty());
101  }
102  {
103  // uVec2
104  auto uni = stage->GetUniform("uVec2");
105  ASSERT_NE(uni, nullptr);
106  EXPECT_EQ(uni->dimensions.rows, 2u);
107  EXPECT_EQ(uni->dimensions.cols, 1u);
108  EXPECT_EQ(uni->location, 1u);
109  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
110  EXPECT_TRUE(uni->padding_layout.empty());
111  }
112  {
113  // uVec3
114  auto uni = stage->GetUniform("uVec3");
115  ASSERT_NE(uni, nullptr);
116  EXPECT_EQ(uni->dimensions.rows, 3u);
117  EXPECT_EQ(uni->dimensions.cols, 1u);
118  EXPECT_EQ(uni->location, 2u);
119  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
120  auto padding = uni->padding_layout;
121  if (GetBackend() == PlaygroundBackend::kMetal) {
122  EXPECT_EQ(padding.size(), 4u);
123  EXPECT_EQ(padding[0], RuntimePaddingType::kFloat);
124  EXPECT_EQ(padding[1], RuntimePaddingType::kFloat);
125  EXPECT_EQ(padding[2], RuntimePaddingType::kFloat);
126  EXPECT_EQ(padding[3], RuntimePaddingType::kPadding);
127  } else {
128  EXPECT_TRUE(padding.empty());
129  }
130  }
131  {
132  // uVec4
133  auto uni = stage->GetUniform("uVec4");
134  ASSERT_NE(uni, nullptr);
135  EXPECT_EQ(uni->dimensions.rows, 4u);
136  EXPECT_EQ(uni->dimensions.cols, 1u);
137  EXPECT_EQ(uni->location, 3u);
138  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
139  EXPECT_TRUE(uni->padding_layout.empty());
140  }
141  {
142  // uMat2
143  auto uni = stage->GetUniform("uMat2");
144  ASSERT_NE(uni, nullptr);
145  EXPECT_EQ(uni->dimensions.rows, 2u);
146  EXPECT_EQ(uni->dimensions.cols, 2u);
147  EXPECT_EQ(uni->location, 4u);
148  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
149  EXPECT_TRUE(uni->padding_layout.empty());
150  }
151  {
152  // uMat3
153  auto uni = stage->GetUniform("uMat3");
154  ASSERT_NE(uni, nullptr);
155  EXPECT_EQ(uni->dimensions.rows, 3u);
156  EXPECT_EQ(uni->dimensions.cols, 3u);
157  EXPECT_EQ(uni->location, 5u);
158  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
159  }
160  {
161  // uMat4
162  auto uni = stage->GetUniform("uMat4");
163  ASSERT_NE(uni, nullptr);
164  EXPECT_EQ(uni->dimensions.rows, 4u);
165  EXPECT_EQ(uni->dimensions.cols, 4u);
166  EXPECT_EQ(uni->location, 6u);
167  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
168  EXPECT_TRUE(uni->padding_layout.empty());
169  }
170  {
171  // uFloatArray
172  auto uni = stage->GetUniform("uFloatArray");
173  ASSERT_NE(uni, nullptr);
174  EXPECT_EQ(uni->dimensions.rows, 1u);
175  EXPECT_EQ(uni->dimensions.cols, 1u);
176  EXPECT_EQ(uni->location, 7u);
177  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
178  EXPECT_TRUE(uni->padding_layout.empty());
179  }
180  {
181  auto uni = stage->GetUniform("uVec2Array");
182  ASSERT_NE(uni, nullptr);
183  EXPECT_EQ(uni->dimensions.rows, 2u);
184  EXPECT_EQ(uni->dimensions.cols, 1u);
185  EXPECT_EQ(uni->location, 9u);
186  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
187  EXPECT_TRUE(uni->padding_layout.empty());
188  }
189  {
190  // uVec3Array
191  auto uni = stage->GetUniform("uVec3Array");
192  ASSERT_NE(uni, nullptr);
193  EXPECT_EQ(uni->dimensions.rows, 3u);
194  EXPECT_EQ(uni->dimensions.cols, 1u);
195  EXPECT_EQ(uni->location, 11u);
196  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
197  }
198  {
199  // uVec4Array
200  auto uni = stage->GetUniform("uVec4Array");
201  ASSERT_NE(uni, nullptr);
202  EXPECT_EQ(uni->dimensions.rows, 4u);
203  EXPECT_EQ(uni->dimensions.cols, 1u);
204  EXPECT_EQ(uni->location, 13u);
205  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
206  EXPECT_TRUE(uni->padding_layout.empty());
207  }
208  {
209  // uMat2Array
210  auto uni = stage->GetUniform("uMat2Array");
211  ASSERT_NE(uni, nullptr);
212  EXPECT_EQ(uni->dimensions.rows, 2u);
213  EXPECT_EQ(uni->dimensions.cols, 2u);
214  EXPECT_EQ(uni->location, 15u);
215  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
216  EXPECT_TRUE(uni->padding_layout.empty());
217  }
218  {
219  // uMat3Array
220  auto uni = stage->GetUniform("uMat3Array");
221  ASSERT_NE(uni, nullptr);
222  EXPECT_EQ(uni->dimensions.rows, 3u);
223  EXPECT_EQ(uni->dimensions.cols, 3u);
224  EXPECT_EQ(uni->location, 17u);
225  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
226  }
227  {
228  // uMat4Array
229  auto uni = stage->GetUniform("uMat4Array");
230  ASSERT_NE(uni, nullptr);
231  EXPECT_EQ(uni->dimensions.rows, 4u);
232  EXPECT_EQ(uni->dimensions.cols, 4u);
233  EXPECT_EQ(uni->location, 19u);
234  EXPECT_EQ(uni->type, RuntimeUniformType::kFloat);
235  EXPECT_TRUE(uni->padding_layout.empty());
236  }
237  break;
238  }
240  EXPECT_EQ(stage->GetUniforms().size(), 1u);
241  const RuntimeUniformDescription* uni =
242  stage->GetUniform(RuntimeStage::kVulkanUBOName);
243  ASSERT_TRUE(uni);
244  EXPECT_EQ(uni->type, RuntimeUniformType::kStruct);
245  EXPECT_EQ(uni->struct_float_count, 35u);
246 
247  EXPECT_EQ(uni->GetGPUSize(), 640u);
248  std::vector<RuntimePaddingType> layout(uni->GetGPUSize() / sizeof(float),
250  // uFloat and uVec2 are packed into a vec4 with 1 byte of padding between.
251  layout[1] = RuntimePaddingType::kPadding;
252  // uVec3 is packed as a vec4 with 1 byte of padding.
253  layout[7] = RuntimePaddingType::kPadding;
254  // uMat2 is packed as two vec4s, with the last 2 bytes of each being
255  // padding.
256  layout[14] = RuntimePaddingType::kPadding;
257  layout[15] = RuntimePaddingType::kPadding;
258  layout[18] = RuntimePaddingType::kPadding;
259  layout[19] = RuntimePaddingType::kPadding;
260  // uMat3 is packed as 3 vec4s, with the last 3 bytes being padding
261  layout[29] = RuntimePaddingType::kPadding;
262  layout[30] = RuntimePaddingType::kPadding;
263  layout[31] = RuntimePaddingType::kPadding;
264  // uFloatArray is packed as 2 vec4s, with the last 3 bytes of each
265  // being padding.
266  layout[49] = RuntimePaddingType::kPadding;
267  layout[50] = RuntimePaddingType::kPadding;
268  layout[51] = RuntimePaddingType::kPadding;
269  layout[53] = RuntimePaddingType::kPadding;
270  layout[54] = RuntimePaddingType::kPadding;
271  layout[55] = RuntimePaddingType::kPadding;
272  // uVec2Array is packed as 2 vec4s, with 2 bytes of padding at the end of
273  // each.
274  layout[58] = RuntimePaddingType::kPadding;
275  layout[59] = RuntimePaddingType::kPadding;
276  layout[62] = RuntimePaddingType::kPadding;
277  layout[63] = RuntimePaddingType::kPadding;
278  // uVec3Array is packed as 2 vec4s, with the last byte of each as padding.
279  layout[67] = RuntimePaddingType::kPadding;
280  layout[71] = RuntimePaddingType::kPadding;
281  // uVec4Array has no padding.
282  // uMat2Array[2] is packed as 4 vec4s, With the last 2 bytes of each being
283  // padding. padding.
284  layout[82] = RuntimePaddingType::kPadding;
285  layout[83] = RuntimePaddingType::kPadding;
286  layout[86] = RuntimePaddingType::kPadding;
287  layout[87] = RuntimePaddingType::kPadding;
288  layout[90] = RuntimePaddingType::kPadding;
289  layout[91] = RuntimePaddingType::kPadding;
290  layout[94] = RuntimePaddingType::kPadding;
291  layout[95] = RuntimePaddingType::kPadding;
292  // uMat3Array[2] is packed as 6 vec4s, with the last 3 bytes of the 3rd
293  // and 6th being padding.
294  layout[105] = RuntimePaddingType::kPadding;
295  layout[106] = RuntimePaddingType::kPadding;
296  layout[107] = RuntimePaddingType::kPadding;
297  layout[117] = RuntimePaddingType::kPadding;
298  layout[118] = RuntimePaddingType::kPadding;
299  layout[119] = RuntimePaddingType::kPadding;
300  // uMat4Array[2] is packed as 8 vec4s with no padding.
301  layout[152] = RuntimePaddingType::kPadding;
302  layout[153] = RuntimePaddingType::kPadding;
303  layout[154] = RuntimePaddingType::kPadding;
304  layout[155] = RuntimePaddingType::kPadding;
305  layout[156] = RuntimePaddingType::kPadding;
306  layout[157] = RuntimePaddingType::kPadding;
307  layout[158] = RuntimePaddingType::kPadding;
308  layout[159] = RuntimePaddingType::kPadding;
309 
310  EXPECT_THAT(uni->padding_layout, ::testing::ElementsAreArray(layout));
311 
312  std::vector<std::pair<std::string, unsigned int>> expected_uniforms = {
313  {"uFloat", 4}, {"uVec2", 8}, {"uVec3", 12},
314  {"uVec4", 16}, {"uMat2", 16}, {"uMat3", 36},
315  {"uMat4", 64}, {"uFloatArray", 8}, {"uVec2Array", 16},
316  {"uVec3Array", 24}, {"uVec4Array", 32}, {"uMat2Array", 32},
317  {"uMat3Array", 72}, {"uMat4Array", 128}};
318 
319  ASSERT_EQ(uni->struct_fields.size(), expected_uniforms.size());
320 
321  for (size_t i = 0; i < expected_uniforms.size(); ++i) {
322  const auto& element = uni->struct_fields[i];
323  const auto& expected = expected_uniforms[i];
324 
325  EXPECT_EQ(element.name, expected.first) << "index: " << i;
326  EXPECT_EQ(element.byte_size, expected.second) << "index: " << i;
327  }
328  break;
329  }
330  }
331 }
332 
333 TEST_P(RuntimeStageTest, CanReadUniformsSamplerBeforeUBO) {
334  if (GetBackend() != PlaygroundBackend::kVulkan) {
335  GTEST_SKIP() << "Test only relevant for Vulkan";
336  }
337  const std::shared_ptr<fml::Mapping> fixture =
338  flutter::testing::OpenFixtureAsMapping(
339  "uniforms_and_sampler_1.frag.iplr");
340  ASSERT_TRUE(fixture);
341  ASSERT_GT(fixture->GetSize(), 0u);
342  auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
343  ABSL_ASSERT_OK(stages);
344  auto stage =
345  stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
346 
347  EXPECT_EQ(stage->GetUniforms().size(), 2u);
348  auto uni = stage->GetUniform(RuntimeStage::kVulkanUBOName);
349  ASSERT_TRUE(uni);
350  // Struct must be offset at 65.
351  EXPECT_EQ(uni->type, RuntimeUniformType::kStruct);
352  EXPECT_EQ(uni->binding, 65u);
353  // Sampler should be offset at 64 but due to current bug
354  // has offset of 0, the correct offset is computed at runtime.
355  auto sampler_uniform = stage->GetUniform("u_texture");
356  EXPECT_EQ(sampler_uniform->type, RuntimeUniformType::kSampledImage);
357  EXPECT_EQ(sampler_uniform->binding, 64u);
358 }
359 
360 TEST_P(RuntimeStageTest, CanReadUniformsSamplerAfterUBO) {
361  if (GetBackend() != PlaygroundBackend::kVulkan) {
362  GTEST_SKIP() << "Test only relevant for Vulkan";
363  }
364  const std::shared_ptr<fml::Mapping> fixture =
365  flutter::testing::OpenFixtureAsMapping(
366  "uniforms_and_sampler_2.frag.iplr");
367  ASSERT_TRUE(fixture);
368  ASSERT_GT(fixture->GetSize(), 0u);
369  auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
370  ABSL_ASSERT_OK(stages);
371  auto stage =
372  stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
373 
374  EXPECT_EQ(stage->GetUniforms().size(), 2u);
375  auto uni = stage->GetUniform(RuntimeStage::kVulkanUBOName);
376  ASSERT_TRUE(uni);
377  // Struct must be offset at 45.
378  EXPECT_EQ(uni->type, RuntimeUniformType::kStruct);
379  EXPECT_EQ(uni->binding, 64u);
380  // Sampler should be offset at 64 but due to current bug
381  // has offset of 0, the correct offset is computed at runtime.
382  auto sampler_uniform = stage->GetUniform("u_texture");
383  EXPECT_EQ(sampler_uniform->type, RuntimeUniformType::kSampledImage);
384  EXPECT_EQ(sampler_uniform->binding, 65u);
385 }
386 
387 TEST_P(RuntimeStageTest, CanRegisterStage) {
388  const std::shared_ptr<fml::Mapping> fixture =
389  flutter::testing::OpenFixtureAsMapping("ink_sparkle.frag.iplr");
390  ASSERT_TRUE(fixture);
391  ASSERT_GT(fixture->GetSize(), 0u);
392  auto stages = RuntimeStage::DecodeRuntimeStages(fixture);
393  ABSL_ASSERT_OK(stages);
394  auto stage =
395  stages.value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
396  ASSERT_TRUE(stage);
397  std::promise<bool> registration;
398  auto future = registration.get_future();
399  auto library = GetContext()->GetShaderLibrary();
400  library->RegisterFunction(
401  stage->GetEntrypoint(), //
402  ToShaderStage(stage->GetShaderStage()), //
403  stage->GetCodeMapping(), //
404  fml::MakeCopyable([reg = std::move(registration)](bool result) mutable {
405  reg.set_value(result);
406  }));
407  ASSERT_TRUE(future.get());
408  {
409  auto function =
410  library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
411  ASSERT_NE(function, nullptr);
412  }
413 
414  // Check if unregistering works.
415 
416  library->UnregisterFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
417  {
418  auto function =
419  library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment);
420  ASSERT_EQ(function, nullptr);
421  }
422 }
423 
424 TEST_P(RuntimeStageTest, CanCreatePipelineFromRuntimeStage) {
425  auto stages_result = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
426  ABSL_ASSERT_OK(stages_result);
427  auto stage =
428  stages_result
429  .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())];
430 
431  ASSERT_TRUE(stage);
432  ASSERT_NE(stage, nullptr);
433  ASSERT_TRUE(RegisterStage(*stage));
434  auto library = GetContext()->GetShaderLibrary();
435  using VS = RuntimeEffectVertexShader;
436  PipelineDescriptor desc;
437  desc.SetLabel("Runtime Stage InkSparkle");
438  desc.AddStageEntrypoint(
439  library->GetFunction(VS::kEntrypointName, ShaderStage::kVertex));
440  desc.AddStageEntrypoint(
441  library->GetFunction(stage->GetEntrypoint(), ShaderStage::kFragment));
442  auto vertex_descriptor = std::make_shared<VertexDescriptor>();
443  vertex_descriptor->SetStageInputs(VS::kAllShaderStageInputs,
444  VS::kInterleavedBufferLayout);
445 
446  std::array<DescriptorSetLayout, 2> descriptor_set_layouts = {
447  VS::kDescriptorSetLayouts[0],
449  .binding = 64u,
450  .descriptor_type = DescriptorType::kUniformBuffer,
451  .shader_stage = ShaderStage::kFragment,
452  },
453  };
454  vertex_descriptor->RegisterDescriptorSetLayouts(descriptor_set_layouts);
455 
456  desc.SetVertexDescriptor(std::move(vertex_descriptor));
458  color0.format = GetContext()->GetCapabilities()->GetDefaultColorFormat();
461  desc.SetColorAttachmentDescriptor(0u, color0);
462  desc.SetStencilAttachmentDescriptors(stencil0);
463  const auto stencil_fmt =
464  GetContext()->GetCapabilities()->GetDefaultStencilFormat();
465  desc.SetStencilPixelFormat(stencil_fmt);
466  auto pipeline = GetContext()->GetPipelineLibrary()->GetPipeline(desc).Get();
467  ASSERT_NE(pipeline, nullptr);
468 }
469 
470 TEST_P(RuntimeStageTest, ContainsExpectedShaderTypes) {
471  auto stages_result = OpenAssetAsRuntimeStage("ink_sparkle.frag.iplr");
472  ABSL_ASSERT_OK(stages_result);
473  auto stages = stages_result.value();
474  // Right now, SkSL gets implicitly bundled regardless of what the build rule
475  // for this test requested. After
476  // https://github.com/flutter/flutter/issues/138919, this may require a build
477  // rule change or a new test.
478  EXPECT_TRUE(stages[RuntimeStageBackend::kSkSL]);
479 
480  EXPECT_TRUE(stages[RuntimeStageBackend::kOpenGLES]);
481  EXPECT_TRUE(stages[RuntimeStageBackend::kMetal]);
482  EXPECT_TRUE(stages[RuntimeStageBackend::kVulkan]);
483 }
484 
485 } // namespace testing
486 } // namespace impeller
PipelineDescriptor & SetStencilPixelFormat(PixelFormat format)
PipelineDescriptor & SetVertexDescriptor(std::shared_ptr< VertexDescriptor > vertex_descriptor)
PipelineDescriptor & AddStageEntrypoint(std::shared_ptr< const ShaderFunction > function)
PipelineDescriptor & SetLabel(std::string_view label)
PipelineDescriptor & SetStencilAttachmentDescriptors(std::optional< StencilAttachmentDescriptor > front_and_back)
PipelineDescriptor & SetColorAttachmentDescriptor(size_t index, ColorAttachmentDescriptor desc)
static const char * kVulkanUBOName
Definition: runtime_stage.h:23
static absl::StatusOr< Map > DecodeRuntimeStages(const std::shared_ptr< fml::Mapping > &payload)
Vector2 padding
The halo padding in source space.
TEST_P(AiksTest, DrawAtlasNoColor)
INSTANTIATE_PLAYGROUND_SUITE(AiksTest)
constexpr RuntimeStageBackend PlaygroundBackendToRuntimeStageBackend(PlaygroundBackend backend)
Definition: playground.h:33
constexpr ShaderStage ToShaderStage(RuntimeShaderStage stage)
Definition: shader_types.h:29
@ kEqual
Comparison test passes if new_value == current_value.
std::shared_ptr< fml::Mapping > CreateMappingFromAllocation(const std::shared_ptr< Allocation > &allocation)
Creates a mapping from allocation.
Definition: allocation.cc:99
LinePipeline::VertexShader VS
Describe the color attachment that will be used with this pipeline.
Definition: formats.h:522
size_t GetGPUSize() const
Computes the total number of bytes that this uniform requires for representation in the GPU.
std::vector< StructField > struct_fields
Definition: runtime_types.h:64
std::vector< RuntimePaddingType > padding_layout
Definition: runtime_types.h:61