10
10
#include " flutter/common/task_runners.h"
11
11
#include " flutter/lib/gpu/context.h"
12
12
#include " flutter/lib/gpu/shader_library.h"
13
+ #include " flutter/lib/gpu/texture.h"
13
14
#include " flutter/runtime/dart_isolate.h"
14
15
#include " flutter/runtime/dart_vm_lifecycle.h"
15
16
#include " flutter/testing/dart_fixture.h"
16
17
#include " flutter/testing/dart_isolate_runner.h"
18
+ #include " flutter/testing/test_dart_native_resolver.h"
17
19
#include " flutter/testing/testing.h"
18
20
#include " fml/memory/ref_ptr.h"
21
+ #include " impeller/fixtures/texture.frag.h"
22
+ #include " impeller/fixtures/texture.vert.h"
19
23
#include " impeller/playground/playground_test.h"
24
+ #include " impeller/renderer/pipeline_library.h"
20
25
#include " impeller/renderer/render_pass.h"
26
+ #include " impeller/renderer/vertex_buffer_builder.h"
21
27
22
28
#include " gtest/gtest.h"
23
29
#include " third_party/imgui/imgui.h"
@@ -46,6 +52,23 @@ class RendererDartTest : public PlaygroundTest,
46
52
isolate_ = CreateDartIsolate ();
47
53
assert (isolate_);
48
54
assert (isolate_->get ()->GetPhase () == flutter::DartIsolate::Phase::Running);
55
+
56
+ // Set up native callbacks.
57
+ //
58
+ // Note: The Dart isolate is configured (by
59
+ // `RendererDartTest::CreateDartIsolate`) to use the main thread, so
60
+ // there's no need for additional synchronization.
61
+ {
62
+ auto set_display_texture = [this ](Dart_NativeArguments args) {
63
+ flutter::gpu::Texture* texture =
64
+ tonic::DartConverter<flutter::gpu::Texture*>::FromDart (
65
+ Dart_GetNativeArgument (args, 0 ));
66
+ assert (texture != nullptr ); // Should always be a valid pointer.
67
+ received_texture_ = texture->GetTexture ();
68
+ };
69
+ AddNativeCallback (" SetDisplayTexture" ,
70
+ CREATE_NATIVE_ENTRY (set_display_texture));
71
+ }
49
72
}
50
73
51
74
flutter::testing::AutoIsolateShutdown* GetIsolate () {
@@ -58,6 +81,169 @@ class RendererDartTest : public PlaygroundTest,
58
81
return isolate_.get ();
59
82
}
60
83
84
+ // / @brief Run a Dart function that's expected to create a texture and pass
85
+ // / it back for rendering via `drawToPlayground`.
86
+ std::shared_ptr<Texture> GetRenderedTextureFromDart (
87
+ const char * dart_function_name) {
88
+ bool success =
89
+ GetIsolate ()->RunInIsolateScope ([this , &dart_function_name]() -> bool {
90
+ Dart_Handle args[] = {tonic::ToDart (GetWindowSize ().width ),
91
+ tonic::ToDart (GetWindowSize ().height )};
92
+ if (tonic::CheckAndHandleError (
93
+ ::Dart_Invoke (Dart_RootLibrary(),
94
+ tonic::ToDart(dart_function_name), 2, args))) {
95
+ return false ;
96
+ }
97
+ return true ;
98
+ });
99
+ if (!success) {
100
+ FML_LOG (ERROR) << " Failed to invoke dart test function:"
101
+ << dart_function_name;
102
+ return nullptr ;
103
+ }
104
+ if (!received_texture_) {
105
+ FML_LOG (ERROR) << " Dart test function `" << dart_function_name
106
+ << " ` did not invoke `drawToPlaygroundSurface`." ;
107
+ return nullptr ;
108
+ }
109
+ return received_texture_;
110
+ }
111
+
112
+ // / @brief Invokes a Dart function.
113
+ // /
114
+ // / Returns false if invoking the function failed or if any unhandled
115
+ // / exceptions were thrown.
116
+ bool RunDartFunction (const char * dart_function_name) {
117
+ return GetIsolate ()->RunInIsolateScope ([&dart_function_name]() -> bool {
118
+ if (tonic::CheckAndHandleError (
119
+ ::Dart_Invoke (Dart_RootLibrary(),
120
+ tonic::ToDart(dart_function_name), 0, nullptr))) {
121
+ return false ;
122
+ }
123
+ return true ;
124
+ });
125
+ }
126
+
127
+ // / @brief Invokes a Dart function with the window's width and height as
128
+ // / arguments.
129
+ // /
130
+ // / Returns false if invoking the function failed or if any unhandled
131
+ // / exceptions were thrown.
132
+ bool RunDartFunctionWithWindowSize (const char * dart_function_name) {
133
+ return GetIsolate ()->RunInIsolateScope (
134
+ [this , &dart_function_name]() -> bool {
135
+ Dart_Handle args[] = {tonic::ToDart (GetWindowSize ().width ),
136
+ tonic::ToDart (GetWindowSize ().height )};
137
+ if (tonic::CheckAndHandleError (
138
+ ::Dart_Invoke (Dart_RootLibrary(),
139
+ tonic::ToDart(dart_function_name), 2, args))) {
140
+ return false ;
141
+ }
142
+ return true ;
143
+ });
144
+ }
145
+
146
+ // / @brief Call a dart function that produces a texture and render the result
147
+ // / in the playground.
148
+ // /
149
+ // / If the playground isn't enabled, the function is simply run once
150
+ // / in order to verify that it doesn't throw any unhandled exceptions.
151
+ bool RenderDartToPlayground (const char * dart_function_name) {
152
+ if (!IsPlaygroundEnabled ()) {
153
+ // If the playground is not enabled, run the function instead to at least
154
+ // verify that it doesn't crash.
155
+ return RunDartFunctionWithWindowSize (dart_function_name);
156
+ }
157
+
158
+ auto context = GetContext ();
159
+ assert (context != nullptr );
160
+
161
+ // ------------------------------------------------------------------------------
162
+ // / Prepare pipeline.
163
+ // /
164
+
165
+ using TextureVS = TextureVertexShader;
166
+ using TextureFS = TextureFragmentShader;
167
+ using TexturePipelineBuilder = PipelineBuilder<TextureVS, TextureFS>;
168
+
169
+ auto pipeline_desc =
170
+ TexturePipelineBuilder::MakeDefaultPipelineDescriptor (*context);
171
+ if (!pipeline_desc.has_value ()) {
172
+ FML_LOG (ERROR) << " Failed to create default pipeline descriptor." ;
173
+ return false ;
174
+ }
175
+ pipeline_desc->SetSampleCount (SampleCount::kCount4 );
176
+ pipeline_desc->SetStencilAttachmentDescriptors (std::nullopt);
177
+ pipeline_desc->SetDepthStencilAttachmentDescriptor (std::nullopt);
178
+ pipeline_desc->SetStencilPixelFormat (PixelFormat::kUnknown );
179
+ pipeline_desc->SetDepthPixelFormat (PixelFormat::kUnknown );
180
+
181
+ auto pipeline =
182
+ context->GetPipelineLibrary ()->GetPipeline (pipeline_desc).Get ();
183
+ if (!pipeline || !pipeline->IsValid ()) {
184
+ FML_LOG (ERROR) << " Failed to create default pipeline." ;
185
+ return false ;
186
+ }
187
+
188
+ // ------------------------------------------------------------------------------
189
+ // / Prepare vertex data.
190
+ // /
191
+
192
+ VertexBufferBuilder<TextureVS::PerVertexData> texture_vtx_builder;
193
+
194
+ // Always stretch out the texture to fill the screen.
195
+
196
+ // clang-format off
197
+ texture_vtx_builder.AddVertices ({
198
+ {{-0.5 , -0.5 , 0.0 }, {0.0 , 0.0 }}, // 1
199
+ {{ 0.5 , -0.5 , 0.0 }, {1.0 , 0.0 }}, // 2
200
+ {{ 0.5 , 0.5 , 0.0 }, {1.0 , 1.0 }}, // 3
201
+ {{-0.5 , -0.5 , 0.0 }, {0.0 , 0.0 }}, // 1
202
+ {{ 0.5 , 0.5 , 0.0 }, {1.0 , 1.0 }}, // 3
203
+ {{-0.5 , 0.5 , 0.0 }, {0.0 , 1.0 }}, // 4
204
+ });
205
+ // clang-format on
206
+
207
+ // ------------------------------------------------------------------------------
208
+ // / Prepare sampler.
209
+ // /
210
+
211
+ const auto & sampler = context->GetSamplerLibrary ()->GetSampler ({});
212
+ if (!sampler) {
213
+ FML_LOG (ERROR) << " Failed to create default sampler." ;
214
+ return false ;
215
+ }
216
+
217
+ // ------------------------------------------------------------------------------
218
+ // / Render to playground.
219
+ // /
220
+
221
+ SinglePassCallback callback = [&](RenderPass& pass) {
222
+ auto texture = GetRenderedTextureFromDart (dart_function_name);
223
+ if (!texture) {
224
+ return false ;
225
+ }
226
+
227
+ auto buffer = HostBuffer::Create (context->GetResourceAllocator ());
228
+
229
+ pass.SetVertexBuffer (texture_vtx_builder.CreateVertexBuffer (
230
+ *context->GetResourceAllocator ()));
231
+
232
+ TextureVS::UniformBuffer uniforms;
233
+ uniforms.mvp = Matrix ();
234
+ TextureVS::BindUniformBuffer (pass, buffer->EmplaceUniform (uniforms));
235
+ TextureFS::BindTextureContents (pass, texture, sampler);
236
+
237
+ pass.SetPipeline (pipeline);
238
+
239
+ if (!pass.Draw ().ok ()) {
240
+ return false ;
241
+ }
242
+ return true ;
243
+ };
244
+ return OpenPlaygroundHere (callback);
245
+ }
246
+
61
247
private:
62
248
std::unique_ptr<flutter::testing::AutoIsolateShutdown> CreateDartIsolate () {
63
249
const auto settings = CreateSettingsForFixture ();
@@ -76,65 +262,46 @@ class RendererDartTest : public PlaygroundTest,
76
262
flutter::DartVMRef vm_ref_;
77
263
fml::RefPtr<fml::TaskRunner> current_task_runner_;
78
264
std::unique_ptr<flutter::testing::AutoIsolateShutdown> isolate_;
265
+
266
+ std::shared_ptr<Texture> received_texture_;
79
267
};
80
268
81
269
INSTANTIATE_PLAYGROUND_SUITE (RendererDartTest);
82
270
83
271
TEST_P (RendererDartTest, CanRunDartInPlaygroundFrame) {
84
- auto isolate = GetIsolate ();
85
-
86
272
SinglePassCallback callback = [&](RenderPass& pass) {
87
273
ImGui::Begin (" Dart test" , nullptr );
88
274
ImGui::Text (
89
275
" This test executes Dart code during the playground frame callback." );
90
276
ImGui::End ();
91
277
92
- return isolate->RunInIsolateScope ([]() -> bool {
93
- if (tonic::CheckAndHandleError (::Dart_Invoke (
94
- Dart_RootLibrary (), tonic::ToDart (" sayHi" ), 0 , nullptr ))) {
95
- return false ;
96
- }
97
- return true ;
98
- });
278
+ return RunDartFunction (" sayHi" );
99
279
};
100
- OpenPlaygroundHere (callback);
280
+ ASSERT_TRUE ( OpenPlaygroundHere (callback) );
101
281
}
102
282
103
- TEST_P (RendererDartTest, CanInstantiateFlutterGPUContext) {
104
- auto isolate = GetIsolate ();
105
- bool result = isolate->RunInIsolateScope ([]() -> bool {
106
- if (tonic::CheckAndHandleError (::Dart_Invoke (
107
- Dart_RootLibrary (), tonic::ToDart (" instantiateDefaultContext" ), 0 ,
108
- nullptr ))) {
109
- return false ;
110
- }
111
- return true ;
112
- });
283
+ // / These test entries correspond to Dart functions located in
284
+ // / `flutter/impeller/fixtures/dart_tests.dart`
113
285
114
- ASSERT_TRUE (result);
286
+ TEST_P (RendererDartTest, CanInstantiateFlutterGPUContext) {
287
+ ASSERT_TRUE (RunDartFunction (" instantiateDefaultContext" ));
115
288
}
116
289
117
- #define DART_TEST_CASE (name ) \
118
- TEST_P (RendererDartTest, name) { \
119
- auto isolate = GetIsolate (); \
120
- bool result = isolate->RunInIsolateScope ([]() -> bool { \
121
- if (tonic::CheckAndHandleError (::Dart_Invoke ( \
122
- Dart_RootLibrary (), tonic::ToDart (#name), 0 , nullptr ))) { \
123
- return false ; \
124
- } \
125
- return true ; \
126
- }); \
127
- ASSERT_TRUE (result); \
128
- }
290
+ TEST_P (RendererDartTest, CanCreateShaderLibrary) {
291
+ ASSERT_TRUE (RunDartFunction (" canCreateShaderLibrary" ));
292
+ }
129
293
130
- // / These test entries correspond to Dart functions located in
131
- // / `flutter/impeller/fixtures/dart_tests.dart`
294
+ TEST_P (RendererDartTest, CanReflectUniformStructs) {
295
+ ASSERT_TRUE (RunDartFunction (" canReflectUniformStructs" ));
296
+ }
132
297
133
- DART_TEST_CASE (canCreateShaderLibrary);
134
- DART_TEST_CASE (canReflectUniformStructs );
135
- DART_TEST_CASE (uniformBindFailsForInvalidHostBufferOffset);
298
+ TEST_P (RendererDartTest, UniformBindFailsForInvalidHostBufferOffset) {
299
+ ASSERT_TRUE ( RunDartFunction ( " uniformBindFailsForInvalidHostBufferOffset " ) );
300
+ }
136
301
137
- DART_TEST_CASE (canCreateRenderPassAndSubmit);
302
+ TEST_P (RendererDartTest, CanCreateRenderPassAndSubmit) {
303
+ ASSERT_TRUE (RenderDartToPlayground (" canCreateRenderPassAndSubmit" ));
304
+ }
138
305
139
306
} // namespace testing
140
307
} // namespace impeller
0 commit comments