Flutter Impeller
content_context_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 <cstdint>
6 #include <future>
7 #include <memory>
8 #include <utility>
9 #include <vector>
10 
11 #include "fml/logging.h"
12 #include "gtest/gtest.h"
13 
18 #include "impeller/core/formats.h"
21 #include "impeller/entity/contents/test/recording_render_pass.h"
32 
33 namespace impeller {
34 namespace testing {
35 
36 namespace {
37 class FakeTexture : public Texture {
38  public:
39  explicit FakeTexture(const TextureDescriptor& desc) : Texture(desc) {}
40 
41  ~FakeTexture() override {}
42 
43  void SetLabel(std::string_view label) {}
44 
45  bool IsValid() const override { return true; }
46 
47  ISize GetSize() const override { return {1, 1}; }
48 
49  Scalar GetYCoordScale() const override { return 1.0; }
50 
51  bool OnSetContents(const uint8_t* contents,
52  size_t length,
53  size_t slice) override {
54  if (GetTextureDescriptor().GetByteSizeOfBaseMipLevel() != length) {
55  return false;
56  }
57  did_set_contents = true;
58  return true;
59  }
60 
61  bool OnSetContents(std::shared_ptr<const fml::Mapping> mapping,
62  size_t slice) override {
63  did_set_contents = true;
64  return true;
65  }
66 
67  bool did_set_contents = false;
68 };
69 
70 class FakeAllocator : public Allocator,
71  public BackendCast<FakeAllocator, Allocator> {
72  public:
73  FakeAllocator() : Allocator() {}
74 
75  uint16_t MinimumBytesPerRow(PixelFormat format) const override { return 0; }
76  ISize GetMaxTextureSizeSupported() const override { return ISize(1, 1); }
77 
78  std::shared_ptr<DeviceBuffer> OnCreateBuffer(
79  const DeviceBufferDescriptor& desc) override {
80  return nullptr;
81  }
82  std::shared_ptr<Texture> OnCreateTexture(
83  const TextureDescriptor& desc) override {
84  if (desc.size == ISize{1, 1}) {
85  auto result = std::make_shared<FakeTexture>(desc);
86  textures.push_back(result);
87  return result;
88  }
89  return nullptr;
90  }
91 
92  std::vector<std::shared_ptr<FakeTexture>> textures = {};
93 };
94 
95 class FakePipeline : public Pipeline<PipelineDescriptor> {
96  public:
97  FakePipeline(std::weak_ptr<PipelineLibrary> library,
98  const PipelineDescriptor& desc)
99  : Pipeline(std::move(library), desc) {}
100 
101  ~FakePipeline() override {}
102 
103  bool IsValid() const override { return true; }
104 };
105 
106 class FakeComputePipeline : public Pipeline<ComputePipelineDescriptor> {
107  public:
108  FakeComputePipeline(std::weak_ptr<PipelineLibrary> library,
109  const ComputePipelineDescriptor& desc)
110  : Pipeline(std::move(library), desc) {}
111 
112  ~FakeComputePipeline() override {}
113 
114  bool IsValid() const override { return true; }
115 };
116 
117 class FakePipelineLibrary : public PipelineLibrary {
118  public:
119  FakePipelineLibrary() {}
120 
121  ~FakePipelineLibrary() override {}
122 
123  bool IsValid() const override { return true; }
124 
125  PipelineFuture<PipelineDescriptor> GetPipeline(
126  PipelineDescriptor descriptor) override {
127  auto pipeline =
128  std::make_shared<FakePipeline>(weak_from_this(), descriptor);
129  std::promise<std::shared_ptr<Pipeline<PipelineDescriptor>>> promise;
130  promise.set_value(std::move(pipeline));
131  return PipelineFuture<PipelineDescriptor>{
132  .descriptor = descriptor,
133  .future =
134  std::shared_future<std::shared_ptr<Pipeline<PipelineDescriptor>>>(
135  promise.get_future())};
136  }
137 
138  PipelineFuture<ComputePipelineDescriptor> GetPipeline(
139  ComputePipelineDescriptor descriptor) override {
140  auto pipeline =
141  std::make_shared<FakeComputePipeline>(weak_from_this(), descriptor);
142  std::promise<std::shared_ptr<Pipeline<ComputePipelineDescriptor>>> promise;
143  promise.set_value(std::move(pipeline));
144  return PipelineFuture<ComputePipelineDescriptor>{
145  .descriptor = descriptor,
146  .future = std::shared_future<
147  std::shared_ptr<Pipeline<ComputePipelineDescriptor>>>(
148  promise.get_future())};
149  }
150 
151  void RemovePipelinesWithEntryPoint(
152  std::shared_ptr<const ShaderFunction> function) {}
153 };
154 
155 class FakeShaderFunction : public ShaderFunction {
156  public:
157  FakeShaderFunction(UniqueID parent_library_id,
158  std::string name,
159  ShaderStage stage)
160  : ShaderFunction(parent_library_id, std::move(name), stage){};
161 
162  ~FakeShaderFunction() override {}
163 };
164 
165 class FakeShaderLibrary : public ShaderLibrary {
166  public:
167  ~FakeShaderLibrary() override {}
168 
169  bool IsValid() const override { return true; }
170 
171  std::shared_ptr<const ShaderFunction> GetFunction(std::string_view name,
172  ShaderStage stage) {
173  return std::make_shared<FakeShaderFunction>(UniqueID{}, std::string(name),
174  stage);
175  }
176 
177  void RegisterFunction(std::string name,
178  ShaderStage stage,
179  std::shared_ptr<fml::Mapping> code,
180  RegistrationCallback callback) override {}
181 
182  void UnregisterFunction(std::string name, ShaderStage stage) override {}
183 };
184 
185 class FakeCommandBuffer : public CommandBuffer {
186  public:
187  explicit FakeCommandBuffer(std::weak_ptr<const Context> context)
188  : CommandBuffer(std::move(context)) {}
189 
190  ~FakeCommandBuffer() {}
191 
192  bool IsValid() const override { return true; }
193 
194  void SetLabel(const std::string& label) const override {}
195 
196  std::shared_ptr<RenderPass> OnCreateRenderPass(
197  RenderTarget render_target) override {
198  return std::make_shared<RecordingRenderPass>(nullptr, context_.lock(),
199  render_target);
200  }
201 
202  std::shared_ptr<BlitPass> OnCreateBlitPass() override { FML_UNREACHABLE() }
203 
204  virtual bool OnSubmitCommands(CompletionCallback callback) { return true; }
205 
206  void OnWaitUntilScheduled() {}
207 
208  std::shared_ptr<ComputePass> OnCreateComputePass() override {
209  FML_UNREACHABLE();
210  }
211 };
212 
213 class FakeContext : public Context,
214  public std::enable_shared_from_this<FakeContext> {
215  public:
216  explicit FakeContext(
217  const std::string& gpu_model = "",
218  PixelFormat default_color_format = PixelFormat::kR8G8B8A8UNormInt)
219  : Context(),
220  allocator_(std::make_shared<FakeAllocator>()),
221  capabilities_(std::shared_ptr<Capabilities>(
222  CapabilitiesBuilder()
223  .SetDefaultColorFormat(default_color_format)
224  .Build())),
225  pipelines_(std::make_shared<FakePipelineLibrary>()),
226  queue_(std::make_shared<CommandQueue>()),
227  shader_library_(std::make_shared<FakeShaderLibrary>()),
228  gpu_model_(gpu_model) {}
229 
230  BackendType GetBackendType() const override { return BackendType::kVulkan; }
231  std::string DescribeGpuModel() const override { return gpu_model_; }
232  bool IsValid() const override { return true; }
233  const std::shared_ptr<const Capabilities>& GetCapabilities() const override {
234  return capabilities_;
235  }
236  std::shared_ptr<Allocator> GetResourceAllocator() const override {
237  return allocator_;
238  }
239  std::shared_ptr<ShaderLibrary> GetShaderLibrary() const {
240  return shader_library_;
241  }
242  std::shared_ptr<SamplerLibrary> GetSamplerLibrary() const { return nullptr; }
243  std::shared_ptr<PipelineLibrary> GetPipelineLibrary() const {
244  return pipelines_;
245  }
246  std::shared_ptr<CommandQueue> GetCommandQueue() const { return queue_; }
247  std::shared_ptr<CommandBuffer> CreateCommandBuffer() const {
248  return std::make_shared<FakeCommandBuffer>(shared_from_this());
249  }
250  void Shutdown() {}
251 
252  private:
253  std::shared_ptr<Allocator> allocator_;
254  std::shared_ptr<const Capabilities> capabilities_;
255  std::shared_ptr<FakePipelineLibrary> pipelines_;
256  std::shared_ptr<CommandQueue> queue_;
257  std::shared_ptr<ShaderLibrary> shader_library_;
258  std::string gpu_model_;
259 };
260 } // namespace
261 
262 TEST(ContentContext, CachesPipelines) {
263  auto context = std::make_shared<FakeContext>();
264 
265  auto create_callback = [&]() {
266  return std::make_shared<FakePipeline>(context->GetPipelineLibrary(),
268  };
269 
270  ContentContext content_context(context, nullptr);
273 
274  auto pipelineA = content_context.GetCachedRuntimeEffectPipeline(
275  "A", optionsA, create_callback);
276 
277  auto pipelineA2 = content_context.GetCachedRuntimeEffectPipeline(
278  "A", optionsA, create_callback);
279 
280  auto pipelineA3 = content_context.GetCachedRuntimeEffectPipeline(
281  "A", optionsB, create_callback);
282 
283  auto pipelineB = content_context.GetCachedRuntimeEffectPipeline(
284  "B", optionsB, create_callback);
285 
286  ASSERT_EQ(pipelineA.get(), pipelineA2.get());
287  ASSERT_NE(pipelineA.get(), pipelineA3.get());
288  ASSERT_NE(pipelineB.get(), pipelineA.get());
289 }
290 
291 TEST(ContentContext, InvalidatesAllPipelinesWithSameUniqueNameOnClear) {
292  auto context = std::make_shared<FakeContext>();
293  ContentContext content_context(context, nullptr);
296 
297  auto create_callback = [&]() {
298  return std::make_shared<FakePipeline>(context->GetPipelineLibrary(),
300  };
301 
302  auto pipelineA = content_context.GetCachedRuntimeEffectPipeline(
303  "A", optionsA, create_callback);
304 
305  auto pipelineA2 = content_context.GetCachedRuntimeEffectPipeline(
306  "A", optionsB, create_callback);
307 
308  auto pipelineB = content_context.GetCachedRuntimeEffectPipeline(
309  "B", optionsB, create_callback);
310 
311  ASSERT_TRUE(pipelineA);
312  ASSERT_TRUE(pipelineA2);
313  ASSERT_TRUE(pipelineB);
314 
315  ASSERT_EQ(pipelineA, content_context.GetCachedRuntimeEffectPipeline(
316  "A", optionsA, create_callback));
317  ASSERT_EQ(pipelineA2, content_context.GetCachedRuntimeEffectPipeline(
318  "A", optionsB, create_callback));
319  ASSERT_EQ(pipelineB, content_context.GetCachedRuntimeEffectPipeline(
320  "B", optionsB, create_callback));
321 
322  content_context.ClearCachedRuntimeEffectPipeline("A");
323 
324  ASSERT_NE(pipelineA, content_context.GetCachedRuntimeEffectPipeline(
325  "A", optionsA, create_callback));
326  ASSERT_NE(pipelineA2, content_context.GetCachedRuntimeEffectPipeline(
327  "A", optionsB, create_callback));
328  ASSERT_EQ(pipelineB, content_context.GetCachedRuntimeEffectPipeline(
329  "B", optionsB, create_callback));
330 
331  content_context.ClearCachedRuntimeEffectPipeline("B");
332 
333  ASSERT_NE(pipelineB, content_context.GetCachedRuntimeEffectPipeline(
334  "B", optionsB, create_callback));
335 }
336 
337 TEST(ContentContext, InitializeCommonlyUsedShadersIfNeeded) {
338  ScopedValidationFatal fatal_validations;
339  // Set a pixel format that is larger than 32bpp.
340  auto context = std::make_shared<FakeContext>("Mali G70",
342  ContentContext content_context(context, nullptr);
343 
344  FakeAllocator& fake_allocator =
345  FakeAllocator::Cast(*context->GetResourceAllocator());
346 
347 #if IMPELLER_ENABLE_3D
348  EXPECT_EQ(fake_allocator.textures.size(), 2u);
349 #else
350  EXPECT_EQ(fake_allocator.textures.size(), 1u);
351 #endif // IMPELLER_ENABLE_3D
352 }
353 
354 } // namespace testing
355 } // namespace impeller
impeller::PipelineDescriptor
Definition: pipeline_descriptor.h:24
impeller::ContentContext::ClearCachedRuntimeEffectPipeline
void ClearCachedRuntimeEffectPipeline(const std::string &unique_entrypoint_name) const
Definition: content_context.cc:590
pipeline.h
shader_function.h
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::CreateCommandBuffer
static id< MTLCommandBuffer > CreateCommandBuffer(id< MTLCommandQueue > queue)
Definition: command_buffer_mtl.mm:117
command_queue.h
impeller::BlendMode::kSource
@ kSource
impeller::PixelFormat::kR8G8B8A8UNormInt
@ kR8G8B8A8UNormInt
texture_descriptor.h
shader_library.h
formats.h
impeller::ShaderStage
ShaderStage
Definition: shader_types.h:22
impeller::ContentContextOptions::blend_mode
BlendMode blend_mode
Definition: content_context.h:333
impeller::ContentContext::GetCachedRuntimeEffectPipeline
std::shared_ptr< Pipeline< PipelineDescriptor > > GetCachedRuntimeEffectPipeline(const std::string &unique_entrypoint_name, const ContentContextOptions &options, const std::function< std::shared_ptr< Pipeline< PipelineDescriptor >>()> &create_callback) const
Definition: content_context.cc:577
impeller::PixelFormat
PixelFormat
The Pixel formats supported by Impeller. The naming convention denotes the usage of the component,...
Definition: formats.h:100
render_pass.h
did_set_contents
bool did_set_contents
Definition: content_context_unittests.cc:67
impeller::testing::TEST
TEST(CanvasRecorder, Save)
Definition: canvas_recorder_unittests.cc:65
backend_cast.h
capabilities.h
impeller::PixelFormat::kR16G16B16A16Float
@ kR16G16B16A16Float
pipeline_library.h
impeller::ISize
TSize< int64_t > ISize
Definition: size.h:138
command_buffer.h
allocator.h
content_context.h
comparable.h
textures
std::vector< std::shared_ptr< FakeTexture > > textures
Definition: content_context_unittests.cc:92
std
Definition: comparable.h:95
impeller::ScopedValidationFatal
Definition: validation.h:48
color.h
pipeline_descriptor.h
device_buffer_descriptor.h
impeller::ContentContextOptions
Definition: content_context.h:288
impeller
Definition: aiks_blur_unittests.cc:20
impeller::ContentContext
Definition: content_context.h:392
impeller::BlendMode::kSourceOver
@ kSourceOver