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