| 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
14 #include "impeller/base/backend_cast.h"
15 #include "impeller/base/comparable.h"
16 #include "impeller/core/allocator.h"
17 #include "impeller/core/device_buffer_descriptor.h"
18 #include "impeller/core/formats.h"
19 #include "impeller/core/texture_descriptor.h"
20 #include "impeller/entity/contents/content_context.h"
21 #include "impeller/entity/contents/test/recording_render_pass.h"
22 #include "impeller/geometry/color.h"
23 #include "impeller/renderer/capabilities.h"
24 #include "impeller/renderer/command_buffer.h"
25 #include "impeller/renderer/command_queue.h"
26 #include "impeller/renderer/pipeline.h"
27 #include "impeller/renderer/pipeline_descriptor.h"
28 #include "impeller/renderer/pipeline_library.h"
29 #include "impeller/renderer/render_pass.h"
30 #include "impeller/renderer/shader_function.h"
31 #include "impeller/renderer/shader_library.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(),
267 PipelineDescriptor{});
268 };
269
270 ContentContext content_context(context, nullptr);
271 ContentContextOptions optionsA{.blend_mode = BlendMode::kSourceOver};
272 ContentContextOptions optionsB{.blend_mode = BlendMode::kSource};
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);
294 ContentContextOptions optionsA{.blend_mode = BlendMode::kSourceOver};
295 ContentContextOptions optionsB{.blend_mode = BlendMode::kSource};
296
297 auto create_callback = [&]() {
298 return std::make_shared<FakePipeline>(context->GetPipelineLibrary(),
299 PipelineDescriptor{});
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",
341 PixelFormat::kR16G16B16A16Float);
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