Flutter macOS Embedder
FlutterEmbedderExternalTextureTest.mm
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 #import <Foundation/Foundation.h>
6 #import <Metal/Metal.h>
7 
8 #include <memory>
9 #include <vector>
10 
11 #import "flutter/display_list/skia/dl_sk_canvas.h"
12 #import "flutter/shell/platform/darwin/graphics/FlutterDarwinContextMetalSkia.h"
13 #import "flutter/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.h"
15 #include "flutter/shell/platform/embedder/embedder.h"
16 #include "flutter/shell/platform/embedder/embedder_external_texture_metal.h"
17 #include "flutter/testing/autoreleasepool_test.h"
18 #include "flutter/testing/testing.h"
19 #include "third_party/googletest/googletest/include/gtest/gtest.h"
20 #include "third_party/skia/include/core/SkImage.h"
21 #include "third_party/skia/include/core/SkSamplingOptions.h"
22 #include "third_party/skia/include/core/SkSurface.h"
23 #include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
24 
25 @interface TestExternalTexture : NSObject <FlutterTexture>
26 
27 - (nonnull instancetype)initWidth:(size_t)width
28  height:(size_t)height
29  pixelFormatType:(OSType)pixelFormatType;
30 
31 @end
32 
33 @implementation TestExternalTexture {
34  size_t _width;
35  size_t _height;
37 }
38 
39 - (nonnull instancetype)initWidth:(size_t)width
40  height:(size_t)height
41  pixelFormatType:(OSType)pixelFormatType {
42  if (self = [super init]) {
43  _width = width;
44  _height = height;
45  _pixelFormatType = pixelFormatType;
46  }
47  return self;
48 }
49 
50 - (CVPixelBufferRef)copyPixelBuffer {
51  return [self pixelBuffer];
52 }
53 
54 - (CVPixelBufferRef)pixelBuffer {
55  NSDictionary* options = @{
56  // This key is required to generate SKPicture with CVPixelBufferRef in metal.
57  (NSString*)kCVPixelBufferMetalCompatibilityKey : @YES
58  };
59  CVPixelBufferRef pxbuffer = NULL;
60  CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, _width, _width, _pixelFormatType,
61  (__bridge CFDictionaryRef)options, &pxbuffer);
62  FML_CHECK(status == kCVReturnSuccess && pxbuffer != nullptr) << "Failed to create pixel buffer";
63  return pxbuffer;
64 }
65 
66 @end
67 
68 namespace flutter::testing {
69 
70 // Test-specific name for AutoreleasePoolTest fixture.
71 using FlutterEmbedderExternalTextureTest = AutoreleasePoolTest;
72 
73 TEST_F(FlutterEmbedderExternalTextureTest, TestTextureResolution) {
74  // Constants.
75  const size_t width = 100;
76  const size_t height = 100;
77  const int64_t texture_id = 1;
78 
79  // Set up the surface.
80  FlutterDarwinContextMetalSkia* darwinContextMetal =
81  [[FlutterDarwinContextMetalSkia alloc] initWithDefaultMTLDevice];
82  SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
83  GrDirectContext* grContext = darwinContextMetal.mainContext.get();
84  sk_sp<SkSurface> gpuSurface(SkSurfaces::RenderTarget(grContext, skgpu::Budgeted::kNo, info));
85 
86  // Create a texture.
87  MTLTextureDescriptor* textureDescriptor = [[MTLTextureDescriptor alloc] init];
88  textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm;
89  textureDescriptor.width = width;
90  textureDescriptor.height = height;
91  textureDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
92  id<MTLTexture> mtlTexture =
93  [darwinContextMetal.device newTextureWithDescriptor:textureDescriptor];
94  std::vector<FlutterMetalTextureHandle> textures = {
95  (__bridge FlutterMetalTextureHandle)mtlTexture,
96  };
97 
98  // Callback to resolve the texture.
99  EmbedderExternalTextureMetal::ExternalTextureCallback callback = [&](int64_t texture_id, size_t w,
100  size_t h) {
101  EXPECT_TRUE(w == width);
102  EXPECT_TRUE(h == height);
103 
104  auto texture = std::make_unique<FlutterMetalExternalTexture>();
105  texture->struct_size = sizeof(FlutterMetalExternalTexture);
106  texture->num_textures = 1;
107  texture->height = h;
108  texture->width = w;
109  texture->pixel_format = FlutterMetalExternalTexturePixelFormat::kRGBA;
110  texture->textures = textures.data();
111  return texture;
112  };
113 
114  // Render the texture.
115  std::unique_ptr<flutter::Texture> texture =
116  std::make_unique<EmbedderExternalTextureMetal>(texture_id, callback);
117  SkRect bounds = SkRect::MakeWH(info.width(), info.height());
118  DlImageSampling sampling = DlImageSampling::kNearestNeighbor;
119  DlSkCanvasAdapter canvas(gpuSurface->getCanvas());
120  flutter::Texture::PaintContext context{
121  .canvas = &canvas,
122  .gr_context = grContext,
123  };
124  texture->Paint(context, bounds, /*freeze=*/false, sampling);
125 
126  ASSERT_TRUE(mtlTexture != nil);
127 
128  gpuSurface->makeImageSnapshot();
129 }
130 
131 TEST_F(FlutterEmbedderExternalTextureTest, TestPopulateExternalTexture) {
132  // Constants.
133  const size_t width = 100;
134  const size_t height = 100;
135  const int64_t texture_id = 1;
136 
137  // Set up the surface.
138  FlutterDarwinContextMetalSkia* darwinContextMetal =
139  [[FlutterDarwinContextMetalSkia alloc] initWithDefaultMTLDevice];
140  SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
141  GrDirectContext* grContext = darwinContextMetal.mainContext.get();
142  sk_sp<SkSurface> gpuSurface(SkSurfaces::RenderTarget(grContext, skgpu::Budgeted::kNo, info));
143 
144  // Create a texture.
145  TestExternalTexture* testExternalTexture =
146  [[TestExternalTexture alloc] initWidth:width
147  height:height
148  pixelFormatType:kCVPixelFormatType_32BGRA];
149  FlutterExternalTexture* textureHolder =
150  [[FlutterExternalTexture alloc] initWithFlutterTexture:testExternalTexture
151  darwinMetalContext:darwinContextMetal];
152 
153  // Callback to resolve the texture.
154  EmbedderExternalTextureMetal::ExternalTextureCallback callback = [&](int64_t texture_id, size_t w,
155  size_t h) {
156  EXPECT_TRUE(w == width);
157  EXPECT_TRUE(h == height);
158 
159  auto texture = std::make_unique<FlutterMetalExternalTexture>();
160  [textureHolder populateTexture:texture.get()];
161 
162  EXPECT_TRUE(texture->num_textures == 1);
163  EXPECT_TRUE(texture->textures != nullptr);
164  EXPECT_TRUE(texture->pixel_format == FlutterMetalExternalTexturePixelFormat::kRGBA);
165  return texture;
166  };
167 
168  // Render the texture.
169  std::unique_ptr<flutter::Texture> texture =
170  std::make_unique<EmbedderExternalTextureMetal>(texture_id, callback);
171  SkRect bounds = SkRect::MakeWH(info.width(), info.height());
172  DlImageSampling sampling = DlImageSampling::kNearestNeighbor;
173  DlSkCanvasAdapter canvas(gpuSurface->getCanvas());
174  flutter::Texture::PaintContext context{
175  .canvas = &canvas,
176  .gr_context = grContext,
177  };
178  texture->Paint(context, bounds, /*freeze=*/false, sampling);
179 
180  gpuSurface->makeImageSnapshot();
181 }
182 
183 TEST_F(FlutterEmbedderExternalTextureTest, TestPopulateExternalTextureYUVA) {
184  // Constants.
185  const size_t width = 100;
186  const size_t height = 100;
187  const int64_t texture_id = 1;
188 
189  // Set up the surface.
190  FlutterDarwinContextMetalSkia* darwinContextMetal =
191  [[FlutterDarwinContextMetalSkia alloc] initWithDefaultMTLDevice];
192  SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
193  GrDirectContext* grContext = darwinContextMetal.mainContext.get();
194  sk_sp<SkSurface> gpuSurface(SkSurfaces::RenderTarget(grContext, skgpu::Budgeted::kNo, info));
195 
196  // Create a texture.
197  TestExternalTexture* testExternalTexture =
198  [[TestExternalTexture alloc] initWidth:width
199  height:height
200  pixelFormatType:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange];
201  FlutterExternalTexture* textureHolder =
202  [[FlutterExternalTexture alloc] initWithFlutterTexture:testExternalTexture
203  darwinMetalContext:darwinContextMetal];
204 
205  // Callback to resolve the texture.
206  EmbedderExternalTextureMetal::ExternalTextureCallback callback = [&](int64_t texture_id, size_t w,
207  size_t h) {
208  EXPECT_TRUE(w == width);
209  EXPECT_TRUE(h == height);
210 
211  auto texture = std::make_unique<FlutterMetalExternalTexture>();
212  [textureHolder populateTexture:texture.get()];
213 
214  EXPECT_TRUE(texture->num_textures == 2);
215  EXPECT_TRUE(texture->textures != nullptr);
216  EXPECT_TRUE(texture->pixel_format == FlutterMetalExternalTexturePixelFormat::kYUVA);
217  EXPECT_TRUE(texture->yuv_color_space ==
218  FlutterMetalExternalTextureYUVColorSpace::kBT601LimitedRange);
219  return texture;
220  };
221 
222  // Render the texture.
223  std::unique_ptr<flutter::Texture> texture =
224  std::make_unique<EmbedderExternalTextureMetal>(texture_id, callback);
225  SkRect bounds = SkRect::MakeWH(info.width(), info.height());
226  DlImageSampling sampling = DlImageSampling::kNearestNeighbor;
227  DlSkCanvasAdapter canvas(gpuSurface->getCanvas());
228  flutter::Texture::PaintContext context{
229  .canvas = &canvas,
230  .gr_context = grContext,
231  };
232  texture->Paint(context, bounds, /*freeze=*/false, sampling);
233 
234  gpuSurface->makeImageSnapshot();
235 }
236 
237 TEST_F(FlutterEmbedderExternalTextureTest, TestPopulateExternalTextureYUVA2) {
238  // Constants.
239  const size_t width = 100;
240  const size_t height = 100;
241  const int64_t texture_id = 1;
242 
243  // Set up the surface.
244  FlutterDarwinContextMetalSkia* darwinContextMetal =
245  [[FlutterDarwinContextMetalSkia alloc] initWithDefaultMTLDevice];
246  SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
247  GrDirectContext* grContext = darwinContextMetal.mainContext.get();
248  sk_sp<SkSurface> gpuSurface(SkSurfaces::RenderTarget(grContext, skgpu::Budgeted::kNo, info));
249 
250  // Create a texture.
251  TestExternalTexture* testExternalTexture =
252  [[TestExternalTexture alloc] initWidth:width
253  height:height
254  pixelFormatType:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange];
255  FlutterExternalTexture* textureHolder =
256  [[FlutterExternalTexture alloc] initWithFlutterTexture:testExternalTexture
257  darwinMetalContext:darwinContextMetal];
258 
259  // Callback to resolve the texture.
260  EmbedderExternalTextureMetal::ExternalTextureCallback callback = [&](int64_t texture_id, size_t w,
261  size_t h) {
262  EXPECT_TRUE(w == width);
263  EXPECT_TRUE(h == height);
264 
265  auto texture = std::make_unique<FlutterMetalExternalTexture>();
266  [textureHolder populateTexture:texture.get()];
267 
268  EXPECT_TRUE(texture->num_textures == 2);
269  EXPECT_TRUE(texture->textures != nullptr);
270  EXPECT_TRUE(texture->pixel_format == FlutterMetalExternalTexturePixelFormat::kYUVA);
271  EXPECT_TRUE(texture->yuv_color_space ==
272  FlutterMetalExternalTextureYUVColorSpace::kBT601FullRange);
273  return texture;
274  };
275 
276  // Render the texture.
277  std::unique_ptr<flutter::Texture> texture =
278  std::make_unique<EmbedderExternalTextureMetal>(texture_id, callback);
279  SkRect bounds = SkRect::MakeWH(info.width(), info.height());
280  DlImageSampling sampling = DlImageSampling::kNearestNeighbor;
281  DlSkCanvasAdapter canvas(gpuSurface->getCanvas());
282  flutter::Texture::PaintContext context{
283  .canvas = &canvas,
284  .gr_context = grContext,
285  };
286  texture->Paint(context, bounds, /*freeze=*/false, sampling);
287 
288  gpuSurface->makeImageSnapshot();
289 }
290 
291 TEST_F(FlutterEmbedderExternalTextureTest, TestPopulateUnsupportedExternalTexture) {
292  // Constants.
293  const size_t width = 100;
294  const size_t height = 100;
295  const int64_t texture_id = 1;
296 
297  // Set up the surface.
298  FlutterDarwinContextMetalSkia* darwinContextMetal =
299  [[FlutterDarwinContextMetalSkia alloc] initWithDefaultMTLDevice];
300  SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
301  GrDirectContext* grContext = darwinContextMetal.mainContext.get();
302  sk_sp<SkSurface> gpuSurface(SkSurfaces::RenderTarget(grContext, skgpu::Budgeted::kNo, info));
303 
304  // Create a texture.
305  TestExternalTexture* testExternalTexture =
306  [[TestExternalTexture alloc] initWidth:width
307  height:height
308  pixelFormatType:kCVPixelFormatType_420YpCbCr8PlanarFullRange];
309  FlutterExternalTexture* textureHolder =
310  [[FlutterExternalTexture alloc] initWithFlutterTexture:testExternalTexture
311  darwinMetalContext:darwinContextMetal];
312 
313  // Callback to resolve the texture.
314  EmbedderExternalTextureMetal::ExternalTextureCallback callback = [&](int64_t texture_id, size_t w,
315  size_t h) {
316  EXPECT_TRUE(w == width);
317  EXPECT_TRUE(h == height);
318 
319  auto texture = std::make_unique<FlutterMetalExternalTexture>();
320  EXPECT_FALSE([textureHolder populateTexture:texture.get()]);
321  return nullptr;
322  };
323 
324  // Render the texture.
325  std::unique_ptr<flutter::Texture> texture =
326  std::make_unique<EmbedderExternalTextureMetal>(texture_id, callback);
327  SkRect bounds = SkRect::MakeWH(info.width(), info.height());
328  DlImageSampling sampling = DlImageSampling::kNearestNeighbor;
329  DlSkCanvasAdapter canvas(gpuSurface->getCanvas());
330  flutter::Texture::PaintContext context{
331  .canvas = &canvas,
332  .gr_context = grContext,
333  };
334  texture->Paint(context, bounds, /*freeze=*/false, sampling);
335 }
336 
337 } // namespace flutter::testing
_pixelFormatType
OSType _pixelFormatType
Definition: FlutterEmbedderExternalTextureTest.mm:36
flutter::testing
Definition: AccessibilityBridgeMacTest.mm:13
_height
size_t _height
Definition: FlutterEmbedderExternalTextureTest.mm:33
FlutterExternalTexture.h
FlutterExternalTexture
Definition: FlutterExternalTexture.h:18
TestExternalTexture
Definition: FlutterEmbedderExternalTextureTest.mm:25
FlutterTexture-p
Definition: FlutterTexture.h:21
flutter::testing::TEST_F
TEST_F(AccessibilityBridgeMacWindowTest, SendsAccessibilityCreateNotificationFlutterViewWindow)
Definition: AccessibilityBridgeMacTest.mm:90
texture_id
int64_t texture_id
Definition: texture_registrar_unittests.cc:24
flutter::testing::FlutterEmbedderExternalTextureTest
AutoreleasePoolTest FlutterEmbedderExternalTextureTest
Definition: FlutterEmbedderExternalTextureTest.mm:71
-[FlutterExternalTexture populateTexture:]
BOOL populateTexture:(nonnull FlutterMetalExternalTexture *metalTexture)