Flutter Windows Embedder
flutter_windows_texture_registrar_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 "flutter/fml/synchronization/waitable_event.h"
6 #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
10 #include "flutter/shell/platform/windows/testing/egl/mock_proc_table.h"
11 #include "flutter/shell/platform/windows/testing/engine_modifier.h"
12 #include "gtest/gtest.h"
13 
14 namespace flutter {
15 namespace testing {
16 
17 using ::testing::_;
18 using ::testing::AtLeast;
19 
20 using Microsoft::WRL::ComPtr;
21 
22 namespace {
23 // Returns an engine instance configured with dummy project path values.
24 std::unique_ptr<FlutterWindowsEngine> GetTestEngine() {
25  FlutterDesktopEngineProperties properties = {};
26  properties.assets_path = L"C:\\foo\\flutter_assets";
27  properties.icu_data_path = L"C:\\foo\\icudtl.dat";
28  properties.aot_library_path = L"C:\\foo\\aot.so";
29  properties.impeller_switch = DefaultImpeller;
30  FlutterProjectBundle project(properties);
31  auto engine = std::make_unique<FlutterWindowsEngine>(project);
32 
33  EngineModifier modifier(engine.get());
34  modifier.embedder_api().RegisterExternalTexture =
35  MOCK_ENGINE_PROC(RegisterExternalTexture,
36  ([](auto engine, auto texture_id) { return kSuccess; }));
37 
38  return engine;
39 }
40 
41 // Creates a ID3D11Texture2D with the specified size.
42 ComPtr<ID3D11Texture2D> CreateD3dTexture(FlutterWindowsEngine* engine,
43  UINT width,
44  UINT height) {
45  ComPtr<ID3D11Device> d3d_device;
46  ComPtr<ID3D11Texture2D> d3d_texture;
47  if (engine->egl_manager()->GetDevice(d3d_device.GetAddressOf())) {
48  D3D11_TEXTURE2D_DESC texture_description = {};
49  texture_description.MipLevels = 1;
50  texture_description.SampleDesc.Count = 1;
51  texture_description.BindFlags = D3D11_BIND_RENDER_TARGET;
52  texture_description.Usage = D3D11_USAGE_DEFAULT;
53  texture_description.CPUAccessFlags = 0;
54  texture_description.ArraySize = 1;
55  texture_description.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
56  texture_description.Height = width;
57  texture_description.Width = height;
58  texture_description.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
59 
60  d3d_device->CreateTexture2D(&texture_description, nullptr,
61  d3d_texture.GetAddressOf());
62  }
63  return d3d_texture;
64 }
65 
66 } // namespace
67 
68 TEST(FlutterWindowsTextureRegistrarTest, CreateDestroy) {
69  std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
70  auto gl = std::make_shared<egl::MockProcTable>();
71  FlutterWindowsTextureRegistrar registrar(engine.get(), gl);
72 
73  EXPECT_TRUE(true);
74 }
75 
76 TEST(FlutterWindowsTextureRegistrarTest, RegisterUnregisterTexture) {
77  std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
78  EngineModifier modifier(engine.get());
79  auto gl = std::make_shared<egl::MockProcTable>();
80 
81  FlutterWindowsTextureRegistrar registrar(engine.get(), gl);
82 
83  FlutterDesktopTextureInfo texture_info = {};
85  texture_info.pixel_buffer_config.callback =
86  [](size_t width, size_t height,
87  void* user_data) -> const FlutterDesktopPixelBuffer* {
88  return nullptr;
89  };
90 
91  int64_t registered_texture_id = 0;
92  bool register_called = false;
93  modifier.embedder_api().RegisterExternalTexture = MOCK_ENGINE_PROC(
94  RegisterExternalTexture, ([&register_called, &registered_texture_id](
95  auto engine, auto texture_id) {
96  register_called = true;
97  registered_texture_id = texture_id;
98  return kSuccess;
99  }));
100 
101  bool unregister_called = false;
102  modifier.embedder_api().UnregisterExternalTexture = MOCK_ENGINE_PROC(
103  UnregisterExternalTexture, ([&unregister_called, &registered_texture_id](
104  auto engine, auto texture_id) {
105  unregister_called = true;
106  EXPECT_EQ(registered_texture_id, texture_id);
107  return kSuccess;
108  }));
109 
110  bool mark_frame_available_called = false;
111  modifier.embedder_api().MarkExternalTextureFrameAvailable =
112  MOCK_ENGINE_PROC(MarkExternalTextureFrameAvailable,
113  ([&mark_frame_available_called, &registered_texture_id](
114  auto engine, auto texture_id) {
115  mark_frame_available_called = true;
116  EXPECT_EQ(registered_texture_id, texture_id);
117  return kSuccess;
118  }));
119 
120  modifier.embedder_api().PostRenderThreadTask =
121  MOCK_ENGINE_PROC(PostRenderThreadTask,
122  [](auto engine, auto callback, void* callback_data) {
123  callback(callback_data);
124  return kSuccess;
125  });
126 
127  auto texture_id = registrar.RegisterTexture(&texture_info);
128  EXPECT_TRUE(register_called);
129  EXPECT_NE(texture_id, -1);
130  EXPECT_EQ(texture_id, registered_texture_id);
131 
132  EXPECT_TRUE(registrar.MarkTextureFrameAvailable(texture_id));
133  EXPECT_TRUE(mark_frame_available_called);
134 
135  fml::AutoResetWaitableEvent latch;
136  registrar.UnregisterTexture(texture_id, [&]() { latch.Signal(); });
137  latch.Wait();
138  ASSERT_TRUE(unregister_called);
139 }
140 
141 TEST(FlutterWindowsTextureRegistrarTest, RegisterUnknownTextureType) {
142  std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
143  auto gl = std::make_shared<egl::MockProcTable>();
144 
145  FlutterWindowsTextureRegistrar registrar(engine.get(), gl);
146 
147  FlutterDesktopTextureInfo texture_info = {};
148  texture_info.type = static_cast<FlutterDesktopTextureType>(1234);
149 
150  auto texture_id = registrar.RegisterTexture(&texture_info);
151 
152  EXPECT_EQ(texture_id, -1);
153 }
154 
155 TEST(FlutterWindowsTextureRegistrarTest, PopulatePixelBufferTexture) {
156  std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
157  auto gl = std::make_shared<egl::MockProcTable>();
158 
159  FlutterWindowsTextureRegistrar registrar(engine.get(), gl);
160 
161  bool release_callback_called = false;
162  size_t width = 100;
163  size_t height = 100;
164  std::unique_ptr<uint8_t[]> pixels =
165  std::make_unique<uint8_t[]>(width * height * 4);
166  FlutterDesktopPixelBuffer pixel_buffer = {};
167  pixel_buffer.width = width;
168  pixel_buffer.height = height;
169  pixel_buffer.buffer = pixels.get();
170  pixel_buffer.release_context = &release_callback_called;
171  pixel_buffer.release_callback = [](void* release_context) {
172  bool* called = reinterpret_cast<bool*>(release_context);
173  *called = true;
174  };
175 
176  FlutterDesktopTextureInfo texture_info = {};
178  texture_info.pixel_buffer_config.user_data = &pixel_buffer;
179  texture_info.pixel_buffer_config.callback =
180  [](size_t width, size_t height,
181  void* user_data) -> const FlutterDesktopPixelBuffer* {
182  return reinterpret_cast<const FlutterDesktopPixelBuffer*>(user_data);
183  };
184 
185  FlutterOpenGLTexture flutter_texture = {};
186  auto texture_id = registrar.RegisterTexture(&texture_info);
187  EXPECT_NE(texture_id, -1);
188 
189  EXPECT_CALL(*gl.get(), GenTextures(1, _))
190  .Times(1)
191  .WillOnce([](GLsizei n, GLuint* textures) { textures[0] = 1; });
192  EXPECT_CALL(*gl.get(), BindTexture).Times(1);
193  EXPECT_CALL(*gl.get(), TexParameteri).Times(AtLeast(1));
194  EXPECT_CALL(*gl.get(), TexImage2D).Times(1);
195  EXPECT_CALL(*gl.get(), DeleteTextures(1, _)).Times(1);
196 
197  auto result =
198  registrar.PopulateTexture(texture_id, 640, 480, &flutter_texture);
199  EXPECT_TRUE(result);
200  EXPECT_EQ(flutter_texture.width, width);
201  EXPECT_EQ(flutter_texture.height, height);
202  EXPECT_EQ(flutter_texture.target, GL_TEXTURE_2D);
203  EXPECT_TRUE(release_callback_called);
204 }
205 
206 TEST(FlutterWindowsTextureRegistrarTest, PopulateD3dTextureWithHandle) {
207  std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
208  auto gl = std::make_shared<egl::MockProcTable>();
209  FlutterWindowsTextureRegistrar registrar(engine.get(), gl);
210 
211  UINT width = 100;
212  UINT height = 100;
213  auto d3d_texture = CreateD3dTexture(engine.get(), width, height);
214  EXPECT_TRUE(d3d_texture);
215 
216  ComPtr<IDXGIResource> shared_resource;
217  EXPECT_TRUE(SUCCEEDED(d3d_texture.As(&shared_resource)));
218 
219  HANDLE shared_handle;
220  EXPECT_TRUE(SUCCEEDED(shared_resource->GetSharedHandle(&shared_handle)));
221 
222  bool release_callback_called = false;
223  FlutterDesktopGpuSurfaceDescriptor surface_descriptor = {};
224  surface_descriptor.struct_size = sizeof(FlutterDesktopGpuSurfaceDescriptor);
225  surface_descriptor.handle = shared_handle;
226  surface_descriptor.width = surface_descriptor.visible_width = width;
227  surface_descriptor.height = surface_descriptor.visible_height = height;
228  surface_descriptor.release_context = &release_callback_called;
229  surface_descriptor.release_callback = [](void* release_context) {
230  bool* called = reinterpret_cast<bool*>(release_context);
231  *called = true;
232  };
233 
234  FlutterDesktopTextureInfo texture_info = {};
235  texture_info.type = kFlutterDesktopGpuSurfaceTexture;
236  texture_info.gpu_surface_config.struct_size =
238  texture_info.gpu_surface_config.type =
240  texture_info.gpu_surface_config.user_data = &surface_descriptor;
241  texture_info.gpu_surface_config.callback =
242  [](size_t width, size_t height,
244  return reinterpret_cast<const FlutterDesktopGpuSurfaceDescriptor*>(
245  user_data);
246  };
247 
248  FlutterOpenGLTexture flutter_texture = {};
249  auto texture_id = registrar.RegisterTexture(&texture_info);
250  EXPECT_NE(texture_id, -1);
251 
252  EXPECT_CALL(*gl.get(), GenTextures(1, _))
253  .Times(1)
254  .WillOnce([](GLsizei n, GLuint* textures) { textures[0] = 1; });
255  EXPECT_CALL(*gl.get(), BindTexture).Times(1);
256  EXPECT_CALL(*gl.get(), TexParameteri).Times(AtLeast(1));
257  EXPECT_CALL(*gl.get(), DeleteTextures(1, _)).Times(1);
258 
259  auto result =
260  registrar.PopulateTexture(texture_id, 640, 480, &flutter_texture);
261  EXPECT_TRUE(result);
262  EXPECT_EQ(flutter_texture.width, width);
263  EXPECT_EQ(flutter_texture.height, height);
264  EXPECT_EQ(flutter_texture.target, GL_TEXTURE_2D);
265  EXPECT_TRUE(release_callback_called);
266 }
267 
268 TEST(FlutterWindowsTextureRegistrarTest, PopulateD3dTexture) {
269  std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
270  auto gl = std::make_shared<egl::MockProcTable>();
271  FlutterWindowsTextureRegistrar registrar(engine.get(), gl);
272 
273  UINT width = 100;
274  UINT height = 100;
275  auto d3d_texture = CreateD3dTexture(engine.get(), width, height);
276  EXPECT_TRUE(d3d_texture);
277 
278  bool release_callback_called = false;
279  FlutterDesktopGpuSurfaceDescriptor surface_descriptor = {};
280  surface_descriptor.struct_size = sizeof(FlutterDesktopGpuSurfaceDescriptor);
281  surface_descriptor.handle = d3d_texture.Get();
282  surface_descriptor.width = surface_descriptor.visible_width = width;
283  surface_descriptor.height = surface_descriptor.visible_height = height;
284  surface_descriptor.release_context = &release_callback_called;
285  surface_descriptor.release_callback = [](void* release_context) {
286  bool* called = reinterpret_cast<bool*>(release_context);
287  *called = true;
288  };
289 
290  FlutterDesktopTextureInfo texture_info = {};
291  texture_info.type = kFlutterDesktopGpuSurfaceTexture;
292  texture_info.gpu_surface_config.struct_size =
294  texture_info.gpu_surface_config.type =
296  texture_info.gpu_surface_config.user_data = &surface_descriptor;
297  texture_info.gpu_surface_config.callback =
298  [](size_t width, size_t height,
300  return reinterpret_cast<const FlutterDesktopGpuSurfaceDescriptor*>(
301  user_data);
302  };
303 
304  FlutterOpenGLTexture flutter_texture = {};
305  auto texture_id = registrar.RegisterTexture(&texture_info);
306  EXPECT_NE(texture_id, -1);
307 
308  EXPECT_CALL(*gl.get(), GenTextures(1, _))
309  .Times(1)
310  .WillOnce([](GLsizei n, GLuint* textures) { textures[0] = 1; });
311  EXPECT_CALL(*gl.get(), BindTexture).Times(1);
312  EXPECT_CALL(*gl.get(), TexParameteri).Times(AtLeast(1));
313  EXPECT_CALL(*gl.get(), DeleteTextures(1, _)).Times(1);
314 
315  auto result =
316  registrar.PopulateTexture(texture_id, 640, 480, &flutter_texture);
317  EXPECT_TRUE(result);
318  EXPECT_EQ(flutter_texture.width, width);
319  EXPECT_EQ(flutter_texture.height, height);
320  EXPECT_EQ(flutter_texture.target, GL_TEXTURE_2D);
321  EXPECT_TRUE(release_callback_called);
322 }
323 
324 TEST(FlutterWindowsTextureRegistrarTest, PopulateInvalidTexture) {
325  std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
326  auto gl = std::make_shared<egl::MockProcTable>();
327 
328  FlutterWindowsTextureRegistrar registrar(engine.get(), gl);
329 
330  auto result = registrar.PopulateTexture(1, 640, 480, nullptr);
331  EXPECT_FALSE(result);
332 }
333 
334 TEST(FlutterWindowsTextureRegistrarTest,
335  UnregisterTextureWithEngineDownInvokesCallback) {
336  std::unique_ptr<FlutterWindowsEngine> engine = GetTestEngine();
337  auto gl = std::make_shared<egl::MockProcTable>();
338 
339  FlutterWindowsTextureRegistrar registrar(engine.get(), gl);
340 
341  fml::AutoResetWaitableEvent latch;
342  registrar.UnregisterTexture(1234, [&]() { latch.Signal(); });
343  latch.Wait();
344 }
345 
346 } // namespace testing
347 } // namespace flutter
bool PopulateTexture(int64_t texture_id, size_t width, size_t height, FlutterOpenGLTexture *texture)
int64_t RegisterTexture(const FlutterDesktopTextureInfo *texture_info)
void UnregisterTexture(int64_t texture_id, fml::closure callback=nullptr)
uint32_t texture_id
@ kFlutterDesktopGpuSurfaceTypeDxgiSharedHandle
@ kFlutterDesktopGpuSurfaceTypeD3d11Texture2D
FlutterDesktopTextureType
@ kFlutterDesktopGpuSurfaceTexture
@ kFlutterDesktopPixelBufferTexture
@ DefaultImpeller
FlutterDesktopBinaryReply callback
TEST(AccessibilityBridgeWindows, GetParent)
FlutterDesktopImpellerSwitch impeller_switch
void(* release_callback)(void *release_context)
FlutterDesktopGpuSurfaceTextureCallback callback
void(* release_callback)(void *release_context)
FlutterDesktopPixelBufferTextureCallback callback
FlutterDesktopGpuSurfaceTextureConfig gpu_surface_config
FlutterDesktopTextureType type
FlutterDesktopPixelBufferTextureConfig pixel_buffer_config