| 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"
14 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterExternalTexture.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;
36 OSType _pixelFormatType;
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)