| 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 #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;
35 OSType _pixelFormatType;
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)