Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
renderer_dart_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#define FML_USED_ON_EMBEDDER
6
7#include <memory>
8
20#include "fml/memory/ref_ptr.h"
21#include "impeller/fixtures/texture.frag.h"
22#include "impeller/fixtures/texture.vert.h"
27
28#include "gtest/gtest.h"
29#include "third_party/imgui/imgui.h"
31
32namespace impeller {
33namespace testing {
34
36 auto fixture =
37 flutter::testing::OpenFixtureAsMapping("playground.shaderbundle");
39 backend_type, std::move(fixture));
41}
42
43// Drains the microtask queue so async Dart entry points run to completion.
44// Returns false if a microtask produced an unhandled error. Must be called
45// inside an isolate scope. The fixture entry points are now async (shader
46// loading returns a Future), and on native everything they await is already
47// available, so the work finishes as microtasks with no message loop needed.
48static bool DrainMicrotasks() {
50 if (queue == nullptr) {
51 return true;
52 }
53 queue->RunMicrotasks();
54 return queue->GetLastError() == tonic::kNoError;
55}
56
59 public:
61 : settings_(CreateSettingsForFixture()),
62 vm_ref_(flutter::DartVMRef::Create(settings_)) {
64
65 current_task_runner_ = fml::MessageLoop::GetCurrent().GetTaskRunner();
66
67 isolate_ = CreateDartIsolate();
68 assert(isolate_);
69 assert(isolate_->get()->GetPhase() == flutter::DartIsolate::Phase::Running);
70
71 // Set up native callbacks.
72 //
73 // Note: The Dart isolate is configured (by
74 // `RendererDartTest::CreateDartIsolate`) to use the main thread, so
75 // there's no need for additional synchronization.
76 {
77 auto set_display_texture = [this](Dart_NativeArguments args) {
80 Dart_GetNativeArgument(args, 0));
81 assert(texture != nullptr); // Should always be a valid pointer.
82 received_texture_ = texture->GetTexture();
83 };
84 AddNativeCallback("SetDisplayTexture",
85 CREATE_NATIVE_ENTRY(set_display_texture));
86 }
87 }
88
90 // Sneak the context into the Flutter GPU API.
91 assert(GetContext() != nullptr);
93
94 InstantiateTestShaderLibrary(GetContext()->GetBackendType());
95
96 return isolate_.get();
97 }
98
99 /// @brief Run a Dart function that's expected to create a texture and pass
100 /// it back for rendering via `drawToPlayground`.
101 std::shared_ptr<Texture> GetRenderedTextureFromDart(
102 const char* dart_function_name) {
103 bool success =
104 GetIsolate()->RunInIsolateScope([this, &dart_function_name]() -> bool {
105 Dart_Handle args[] = {tonic::ToDart(GetWindowSize().width),
108 ::Dart_Invoke(Dart_RootLibrary(),
109 tonic::ToDart(dart_function_name), 2, args))) {
110 return false;
111 }
112 return DrainMicrotasks();
113 });
114 if (!success) {
115 FML_LOG(ERROR) << "Failed to invoke dart test function:"
116 << dart_function_name;
117 return nullptr;
118 }
119 if (!received_texture_) {
120 FML_LOG(ERROR) << "Dart test function `" << dart_function_name
121 << "` did not invoke `drawToPlaygroundSurface`.";
122 return nullptr;
123 }
124 return received_texture_;
125 }
126
127 /// @brief Invokes a Dart function.
128 ///
129 /// Returns false if invoking the function failed or if any unhandled
130 /// exceptions were thrown.
131 bool RunDartFunction(const char* dart_function_name) {
132 return GetIsolate()->RunInIsolateScope([&dart_function_name]() -> bool {
134 ::Dart_Invoke(Dart_RootLibrary(),
135 tonic::ToDart(dart_function_name), 0, nullptr))) {
136 return false;
137 }
138 return DrainMicrotasks();
139 });
140 }
141
142 /// @brief Invokes a Dart function with the window's width and height as
143 /// arguments.
144 ///
145 /// Returns false if invoking the function failed or if any unhandled
146 /// exceptions were thrown.
147 bool RunDartFunctionWithWindowSize(const char* dart_function_name) {
149 [this, &dart_function_name]() -> bool {
150 Dart_Handle args[] = {tonic::ToDart(GetWindowSize().width),
153 ::Dart_Invoke(Dart_RootLibrary(),
154 tonic::ToDart(dart_function_name), 2, args))) {
155 return false;
156 }
157 return DrainMicrotasks();
158 });
159 }
160
161 /// @brief Call a dart function that produces a texture and render the result
162 /// in the playground.
163 ///
164 /// If the playground isn't enabled, the function is simply run once
165 /// in order to verify that it doesn't throw any unhandled exceptions.
166 bool RenderDartToPlayground(const char* dart_function_name) {
167 if (!IsPlaygroundEnabled()) {
168 // If the playground is not enabled, run the function instead to at least
169 // verify that it doesn't crash.
170 return RunDartFunctionWithWindowSize(dart_function_name);
171 }
172
173 auto context = GetContext();
174 assert(context != nullptr);
175
176 //------------------------------------------------------------------------------
177 /// Prepare pipeline.
178 ///
179
180 using TextureVS = TextureVertexShader;
181 using TextureFS = TextureFragmentShader;
182 using TexturePipelineBuilder = PipelineBuilder<TextureVS, TextureFS>;
183
184 auto pipeline_desc =
185 TexturePipelineBuilder::MakeDefaultPipelineDescriptor(*context);
186 if (!pipeline_desc.has_value()) {
187 FML_LOG(ERROR) << "Failed to create default pipeline descriptor.";
188 return false;
189 }
190 pipeline_desc->SetSampleCount(SampleCount::kCount4);
191 pipeline_desc->SetStencilAttachmentDescriptors(std::nullopt);
192 pipeline_desc->SetDepthStencilAttachmentDescriptor(std::nullopt);
193 pipeline_desc->SetStencilPixelFormat(PixelFormat::kUnknown);
194 pipeline_desc->SetDepthPixelFormat(PixelFormat::kUnknown);
195
196 auto pipeline =
197 context->GetPipelineLibrary()->GetPipeline(pipeline_desc).Get();
198 if (!pipeline || !pipeline->IsValid()) {
199 FML_LOG(ERROR) << "Failed to create default pipeline.";
200 return false;
201 }
202
203 //------------------------------------------------------------------------------
204 /// Prepare vertex data.
205 ///
206
208
209 // Always stretch out the texture to fill the screen.
210
211 // clang-format off
212 texture_vtx_builder.AddVertices({
213 {{-0.5, -0.5, 0.0}, {0.0, 0.0}}, // 1
214 {{ 0.5, -0.5, 0.0}, {1.0, 0.0}}, // 2
215 {{ 0.5, 0.5, 0.0}, {1.0, 1.0}}, // 3
216 {{-0.5, -0.5, 0.0}, {0.0, 0.0}}, // 1
217 {{ 0.5, 0.5, 0.0}, {1.0, 1.0}}, // 3
218 {{-0.5, 0.5, 0.0}, {0.0, 1.0}}, // 4
219 });
220 // clang-format on
221
222 //------------------------------------------------------------------------------
223 /// Prepare sampler.
224 ///
225
226 const auto& sampler = context->GetSamplerLibrary()->GetSampler({});
227 if (!sampler) {
228 FML_LOG(ERROR) << "Failed to create default sampler.";
229 return false;
230 }
231
232 //------------------------------------------------------------------------------
233 /// Render to playground.
234 ///
235
237 auto texture = GetRenderedTextureFromDart(dart_function_name);
238 if (!texture) {
239 return false;
240 }
241
243 context->GetResourceAllocator(), context->GetIdleWaiter(),
244 context->GetCapabilities()->GetMinimumUniformAlignment());
245
246 pass.SetVertexBuffer(texture_vtx_builder.CreateVertexBuffer(
247 *context->GetResourceAllocator()));
248
249 TextureVS::UniformBuffer uniforms;
250 uniforms.mvp = Matrix();
251 TextureVS::BindUniformBuffer(pass, buffer->EmplaceUniform(uniforms));
252 TextureFS::BindTextureContents(pass, texture, sampler);
253
254 pass.SetPipeline(pipeline);
255
256 if (!pass.Draw().ok()) {
257 return false;
258 }
259 return true;
260 };
262 }
263
264 private:
265 std::unique_ptr<flutter::testing::AutoIsolateShutdown> CreateDartIsolate() {
266 const auto settings = CreateSettingsForFixture();
268 current_task_runner_, //
269 current_task_runner_, //
270 current_task_runner_, //
271 current_task_runner_ //
272 );
274 vm_ref_, settings, task_runners, "main", {},
276 }
277
278 const flutter::Settings settings_;
279 flutter::DartVMRef vm_ref_;
280 fml::RefPtr<fml::TaskRunner> current_task_runner_;
281 std::unique_ptr<flutter::testing::AutoIsolateShutdown> isolate_;
282
283 std::shared_ptr<Texture> received_texture_;
284};
285
287
288TEST_P(RendererDartTest, CanRunDartInPlaygroundFrame) {
289 SinglePassCallback callback = [&](RenderPass& pass) {
290 ImGui::Begin("Dart test", nullptr);
291 ImGui::Text(
292 "This test executes Dart code during the playground frame callback.");
293 ImGui::End();
294
295 return RunDartFunction("sayHi");
296 };
297 ASSERT_TRUE(OpenPlaygroundHere(callback));
298}
299
300/// These test entries correspond to Dart functions located in
301/// `flutter/impeller/fixtures/dart_tests.dart`
302
303TEST_P(RendererDartTest, CanInstantiateFlutterGPUContext) {
304 ASSERT_TRUE(RunDartFunction("instantiateDefaultContext"));
305}
306
307TEST_P(RendererDartTest, CanCreateShaderLibrary) {
308 ASSERT_TRUE(RunDartFunction("canCreateShaderLibrary"));
309}
310
311TEST_P(RendererDartTest, CanReflectUniformStructs) {
312 ASSERT_TRUE(RunDartFunction("canReflectUniformStructs"));
313}
314
315TEST_P(RendererDartTest, CanCreateRenderPassAndSubmit) {
316 ASSERT_TRUE(RenderDartToPlayground("canCreateRenderPassAndSubmit"));
317}
318
319} // namespace testing
320} // namespace impeller
static void SetOverrideContext(std::shared_ptr< impeller::Context > context)
Definition context.cc:31
static void SetOverride(fml::RefPtr< ShaderLibrary > override_shader_library)
Sets a return override for MakeFromAsset for testing purposes.
static fml::RefPtr< ShaderLibrary > MakeFromFlatbuffer(impeller::Context::BackendType backend_type, std::shared_ptr< fml::Mapping > payload, std::string library_id="")
bool RunInIsolateScope(const std::function< bool(void)> &closure)
virtual Settings CreateSettingsForFixture()
void AddNativeCallback(const std::string &name, Dart_NativeFunction callback)
static void EnsureInitializedForCurrentThread()
fml::RefPtr< fml::TaskRunner > GetTaskRunner() const
static FML_EMBEDDER_ONLY MessageLoop & GetCurrent()
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)
bool OpenPlaygroundHere(const RenderCallback &render_callback)
bool IsPlaygroundEnabled() const
ISize GetWindowSize() const
std::function< bool(RenderPass &pass)> SinglePassCallback
Definition playground.h:38
std::shared_ptr< Context > GetContext() const
Definition playground.cc:96
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition render_pass.h:30
VertexBuffer CreateVertexBuffer(HostBuffer &data_host_buffer, HostBuffer &indexes_host_buffer) const
VertexBufferBuilder & AddVertices(std::initializer_list< VertexType_ > vertices)
bool RunDartFunctionWithWindowSize(const char *dart_function_name)
Invokes a Dart function with the window's width and height as arguments.
std::shared_ptr< Texture > GetRenderedTextureFromDart(const char *dart_function_name)
Run a Dart function that's expected to create a texture and pass it back for rendering via drawToPlay...
bool RenderDartToPlayground(const char *dart_function_name)
Call a dart function that produces a texture and render the result in the playground.
bool RunDartFunction(const char *dart_function_name)
Invokes a Dart function.
flutter::testing::AutoIsolateShutdown * GetIsolate()
static DartMicrotaskQueue * GetForCurrentThread()
VkQueue queue
Definition main.cc:71
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlutterDesktopBinaryReply callback
#define FML_LOG(severity)
Definition logging.h:101
FlTexture * texture
std::string GetCurrentTestName()
Gets the name of the currently running test. This is useful in generating logs or assets based on tes...
Definition testing.cc:14
std::string GetDefaultKernelFilePath()
Returns the default path to kernel_blob.bin. This file is within the directory returned by GetFixture...
Definition testing.cc:18
std::unique_ptr< AutoIsolateShutdown > RunDartCodeInIsolate(DartVMRef &vm_ref, const Settings &settings, const TaskRunners &task_runners, std::string entrypoint, const std::vector< std::string > &args, const std::string &kernel_file_path, fml::WeakPtr< IOManager > io_manager, std::unique_ptr< PlatformConfiguration > platform_configuration)
std::unique_ptr< fml::Mapping > OpenFixtureAsMapping(const std::string &fixture_name)
Opens a fixture of the given file name and returns a mapping to its contents.
Definition testing.cc:58
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 void InstantiateTestShaderLibrary(Context::BackendType backend_type)
TEST_P(AiksTest, DrawAtlasNoColor)
Dart_Handle ToDart(const T &object)
@ kNoError
Definition dart_error.h:68
bool CheckAndHandleError(Dart_Handle handle)
Definition dart_error.cc:33
#define INSTANTIATE_PLAYGROUND_SUITE(playground)
std::shared_ptr< ContextGLES > context
std::shared_ptr< PipelineGLES > pipeline
int32_t height
int32_t width
A 4x4 matrix using column-major storage.
Definition matrix.h:37
An optional (but highly recommended) utility for creating pipelines from reflected shader information...
#define CREATE_NATIVE_ENTRY(native_entry)