Flutter Engine
 
Loading...
Searching...
No Matches
FlutterDarwinExternalTextureMetal.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
6
14#include "third_party/skia/include/core/SkColorSpace.h"
15#include "third_party/skia/include/core/SkImage.h"
16#include "third_party/skia/include/core/SkYUVAInfo.h"
17#include "third_party/skia/include/gpu/GpuTypes.h"
18#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
19#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
20#include "third_party/skia/include/gpu/ganesh/GrYUVABackendTextures.h"
21#include "third_party/skia/include/gpu/ganesh/SkImageGanesh.h"
22#include "third_party/skia/include/gpu/ganesh/mtl/GrMtlBackendSurface.h"
23#include "third_party/skia/include/gpu/ganesh/mtl/GrMtlTypes.h"
24#include "third_party/skia/include/ports/SkCFObject.h"
25
27
30 NSObject<FlutterTexture>* _externalTexture;
32 sk_sp<flutter::DlImage> _externalImage;
33 CVPixelBufferRef _lastPixelBuffer;
36}
37
38- (instancetype)initWithTextureCache:(nonnull CVMetalTextureCacheRef)textureCache
39 textureID:(int64_t)textureID
40 texture:(NSObject<FlutterTexture>*)texture
41 enableImpeller:(BOOL)enableImpeller {
42 if (self = [super init]) {
43 _textureCache.Retain(textureCache);
44 _textureID = textureID;
46 _enableImpeller = enableImpeller;
47 return self;
48 }
49 return nil;
50}
51
52- (void)dealloc {
53 CVPixelBufferRelease(_lastPixelBuffer);
54 if (_textureCache) {
55 CVMetalTextureCacheFlush(_textureCache, /* options (must be zero) */ 0);
56 }
57}
58
59- (void)paintContext:(flutter::Texture::PaintContext&)context
60 bounds:(const SkRect&)bounds
61 freeze:(BOOL)freeze
62 sampling:(const flutter::DlImageSampling)sampling {
63 const bool needsUpdatedTexture = (!freeze && _textureFrameAvailable) || !_externalImage;
64
65 if (needsUpdatedTexture) {
66 [self onNeedsUpdatedTexture:context];
67 }
68
69 if (_externalImage) {
70 context.canvas->DrawImageRect(
71 _externalImage, // image
72 flutter::DlRect::Make(_externalImage->GetBounds()), // source rect
73 flutter::ToDlRect(bounds), // destination rect
74 sampling, // sampling
75 context.paint, // paint
77 );
78 }
79}
80
81- (void)onNeedsUpdatedTexture:(flutter::Texture::PaintContext&)context {
82 CVPixelBufferRef pixelBuffer = [_externalTexture copyPixelBuffer];
83 if (pixelBuffer) {
84 CVPixelBufferRelease(_lastPixelBuffer);
85 _lastPixelBuffer = pixelBuffer;
86 _pixelFormat = CVPixelBufferGetPixelFormatType(_lastPixelBuffer);
87 }
88
89 // If the application told us there was a texture frame available but did not provide one when
90 // asked for it, reuse the previous texture but make sure to ask again the next time around.
91 sk_sp<flutter::DlImage> image = [self wrapExternalPixelBuffer:_lastPixelBuffer context:context];
92 if (image) {
95 }
96}
97
98- (void)onGrContextCreated {
99 // External images in this backend have no thread affinity and are not tied to the context in any
100 // way. Instead, they are tied to the Metal device which is associated with the cache already and
101 // is consistent throughout the shell run.
102}
103
104- (void)onGrContextDestroyed {
105 // The image must be reset because it is tied to the onscreen context. But the pixel buffer that
106 // created the image is still around. In case of context reacquisition, that last pixel
107 // buffer will be used to materialize the image in case the application fails to provide a new
108 // one.
109 _externalImage.reset();
110 if (_textureCache) {
111 CVMetalTextureCacheFlush(_textureCache, /* options (must be zero) */ 0);
112 }
113}
114
115- (void)markNewFrameAvailable {
117}
118
119- (void)onTextureUnregistered {
120 if ([_externalTexture respondsToSelector:@selector(onTextureUnregistered:)]) {
121 [_externalTexture onTextureUnregistered:_externalTexture];
122 }
123}
124
125#pragma mark - External texture skia wrapper methods.
126
127- (sk_sp<flutter::DlImage>)wrapExternalPixelBuffer:(CVPixelBufferRef)pixelBuffer
128 context:(flutter::Texture::PaintContext&)context {
129 if (!pixelBuffer) {
130 return nullptr;
131 }
132
133 sk_sp<flutter::DlImage> image = nullptr;
134 if (_pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ||
135 _pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
136 image = [self wrapNV12ExternalPixelBuffer:pixelBuffer context:context];
137 } else if (_pixelFormat == kCVPixelFormatType_32BGRA) {
138 image = [self wrapBGRAExternalPixelBuffer:pixelBuffer context:context];
139 } else {
140 FML_LOG(ERROR) << "Unsupported pixel format: " << _pixelFormat;
141 return nullptr;
142 }
143
144 if (!image) {
145 FML_DLOG(ERROR) << "Could not wrap Metal texture as a display list image.";
146 }
147
148 return image;
149}
150
151- (sk_sp<flutter::DlImage>)wrapNV12ExternalPixelBuffer:(CVPixelBufferRef)pixelBuffer
152 context:(flutter::Texture::PaintContext&)context {
153 SkISize textureSize =
154 SkISize::Make(CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer));
155 CVMetalTextureRef yMetalTexture = nullptr;
156 {
157 CVReturn cvReturn =
158 CVMetalTextureCacheCreateTextureFromImage(/*allocator=*/kCFAllocatorDefault,
159 /*textureCache=*/_textureCache,
160 /*sourceImage=*/pixelBuffer,
161 /*textureAttributes=*/nullptr,
162 /*pixelFormat=*/MTLPixelFormatR8Unorm,
163 /*width=*/textureSize.width(),
164 /*height=*/textureSize.height(),
165 /*planeIndex=*/0u,
166 /*texture=*/&yMetalTexture);
167
168 if (cvReturn != kCVReturnSuccess) {
169 FML_DLOG(ERROR) << "Could not create Metal texture from pixel buffer: CVReturn " << cvReturn;
170 return nullptr;
171 }
172 }
173
174 CVMetalTextureRef uvMetalTexture = nullptr;
175 {
176 CVReturn cvReturn =
177 CVMetalTextureCacheCreateTextureFromImage(/*allocator=*/kCFAllocatorDefault,
178 /*textureCache=*/_textureCache,
179 /*sourceImage=*/pixelBuffer,
180 /*textureAttributes=*/nullptr,
181 /*pixelFormat=*/MTLPixelFormatRG8Unorm,
182 /*width=*/textureSize.width() / 2,
183 /*height=*/textureSize.height() / 2,
184 /*planeIndex=*/1u,
185 /*texture=*/&uvMetalTexture);
186
187 if (cvReturn != kCVReturnSuccess) {
188 FML_DLOG(ERROR) << "Could not create Metal texture from pixel buffer: CVReturn " << cvReturn;
189 return nullptr;
190 }
191 }
192
193 id<MTLTexture> yTex = CVMetalTextureGetTexture(yMetalTexture);
194 CVBufferRelease(yMetalTexture);
195
196 id<MTLTexture> uvTex = CVMetalTextureGetTexture(uvMetalTexture);
197 CVBufferRelease(uvMetalTexture);
198
199 if (_enableImpeller) {
200 impeller::YUVColorSpace yuvColorSpace =
201 _pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
205 UVTex:uvTex
206 YUVColorSpace:yuvColorSpace
207 aiksContext:context.aiks_context];
208 }
209
210 SkYUVColorSpace colorSpace = _pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
211 ? kRec601_Limited_SkYUVColorSpace
212 : kJPEG_Full_SkYUVColorSpace;
214 UVTex:uvTex
215 YUVColorSpace:colorSpace
216 grContext:context.gr_context
217 width:textureSize.width()
218 height:textureSize.height()];
219 if (!skImage) {
220 return nullptr;
221 }
222
223 // This image should not escape local use by this flutter::Texture implementation
224 return flutter::DlImage::Make(skImage);
225}
226
227- (sk_sp<flutter::DlImage>)wrapBGRAExternalPixelBuffer:(CVPixelBufferRef)pixelBuffer
228 context:(flutter::Texture::PaintContext&)context {
229 SkISize textureSize =
230 SkISize::Make(CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer));
231 CVMetalTextureRef metalTexture = nullptr;
232 CVReturn cvReturn =
233 CVMetalTextureCacheCreateTextureFromImage(/*allocator=*/kCFAllocatorDefault,
234 /*textureCache=*/_textureCache,
235 /*sourceImage=*/pixelBuffer,
236 /*textureAttributes=*/nullptr,
237 /*pixelFormat=*/MTLPixelFormatBGRA8Unorm,
238 /*width=*/textureSize.width(),
239 /*height=*/textureSize.height(),
240 /*planeIndex=*/0u,
241 /*texture=*/&metalTexture);
242
243 if (cvReturn != kCVReturnSuccess) {
244 FML_DLOG(ERROR) << "Could not create Metal texture from pixel buffer: CVReturn " << cvReturn;
245 return nullptr;
246 }
247
248 id<MTLTexture> rgbaTex = CVMetalTextureGetTexture(metalTexture);
249 CVBufferRelease(metalTexture);
250
251 if (_enableImpeller) {
253 aiksContext:context.aiks_context];
254 }
255
257 grContext:context.gr_context
258 width:textureSize.width()
259 height:textureSize.height()];
260 if (!skImage) {
261 return nullptr;
262 }
263
264 // This image should not escape local use by this flutter::Texture implementation
265 return flutter::DlImage::Make(skImage);
266}
267
268@end
269
271
272+ (sk_sp<SkImage>)wrapYUVATexture:(id<MTLTexture>)yTex
273 UVTex:(id<MTLTexture>)uvTex
274 YUVColorSpace:(SkYUVColorSpace)colorSpace
275 grContext:(nonnull GrDirectContext*)grContext
276 width:(size_t)width
277 height:(size_t)height {
278#if SLIMPELLER
279 return nullptr;
280#else // SLIMPELLER
281 GrMtlTextureInfo ySkiaTextureInfo;
282 ySkiaTextureInfo.fTexture.retain((__bridge GrMTLHandle)yTex);
283
284 GrBackendTexture skiaBackendTextures[2];
285 skiaBackendTextures[0] =
286 GrBackendTextures::MakeMtl(width, height, skgpu::Mipmapped::kNo, ySkiaTextureInfo);
287
288 GrMtlTextureInfo uvSkiaTextureInfo;
289 uvSkiaTextureInfo.fTexture.retain((__bridge GrMTLHandle)uvTex);
290
291 skiaBackendTextures[1] =
292 GrBackendTextures::MakeMtl(width, height, skgpu::Mipmapped::kNo, uvSkiaTextureInfo);
293 SkYUVAInfo yuvaInfo(skiaBackendTextures[0].dimensions(), SkYUVAInfo::PlaneConfig::kY_UV,
294 SkYUVAInfo::Subsampling::k444, colorSpace);
295 GrYUVABackendTextures yuvaBackendTextures(yuvaInfo, skiaBackendTextures,
296 kTopLeft_GrSurfaceOrigin);
297
298 return SkImages::TextureFromYUVATextures(grContext, yuvaBackendTextures,
299 /*imageColorSpace=*/nullptr,
300 /*releaseProc*/ nullptr, /*releaseContext*/ nullptr);
301#endif // SLIMPELLER
302}
303
304+ (sk_sp<SkImage>)wrapRGBATexture:(id<MTLTexture>)rgbaTex
305 grContext:(nonnull GrDirectContext*)grContext
306 width:(size_t)width
307 height:(size_t)height {
308#if SLIMPELLER
309 return nullptr;
310#else // SLIMPELLER
311
312 GrMtlTextureInfo skiaTextureInfo;
313 skiaTextureInfo.fTexture.retain((__bridge GrMTLHandle)rgbaTex);
314
315 GrBackendTexture skiaBackendTexture =
316 GrBackendTextures::MakeMtl(width, height, skgpu::Mipmapped ::kNo, skiaTextureInfo);
317
318 return SkImages::BorrowTextureFrom(grContext, skiaBackendTexture, kTopLeft_GrSurfaceOrigin,
319 kBGRA_8888_SkColorType, kPremul_SkAlphaType,
320 /*colorSpace=*/nullptr, /*releaseProc*/ nullptr,
321 /*releaseContext*/ nullptr);
322#endif // SLIMPELLER
323}
324@end
325
327
328+ (sk_sp<flutter::DlImage>)wrapYUVATexture:(id<MTLTexture>)yTex
329 UVTex:(id<MTLTexture>)uvTex
330 YUVColorSpace:(impeller::YUVColorSpace)colorSpace
331 aiksContext:(nonnull impeller::AiksContext*)aiks_context {
335 yDesc.size = impeller::ISize(yTex.width, yTex.height);
336 yDesc.mip_count = 1;
337 auto yTexture = impeller::TextureMTL::Wrapper(yDesc, yTex);
338 yTexture->SetCoordinateSystem(impeller::TextureCoordinateSystem::kUploadFromHost);
339
343 uvDesc.size = impeller::ISize(uvTex.width, uvTex.height);
344 uvDesc.mip_count = 1;
345 auto uvTexture = impeller::TextureMTL::Wrapper(uvDesc, uvTex);
346 uvTexture->SetCoordinateSystem(impeller::TextureCoordinateSystem::kUploadFromHost);
347 ;
348
349 return impeller::DlImageImpeller::MakeFromYUVTextures(aiks_context, yTexture, uvTexture,
350 colorSpace);
351}
352
353+ (sk_sp<flutter::DlImage>)wrapRGBATexture:(id<MTLTexture>)rgbaTex
354 aiksContext:(nonnull impeller::AiksContext*)aiks_context {
358 desc.size = impeller::ISize(rgbaTex.width, rgbaTex.height);
359 desc.mip_count = 1;
360 auto texture = impeller::TextureMTL::Wrapper(desc, rgbaTex);
363}
364@end
sk_sp< flutter::DlImage > _externalImage
NSObject< FlutterTexture > * _externalTexture
CVPixelBufferRef _lastPixelBuffer
int64_t _textureID
static sk_sp< DlImage > Make(const SkImage *image)
Definition dl_image.cc:11
static sk_sp< DlImageImpeller > MakeFromYUVTextures(AiksContext *aiks_context, std::shared_ptr< Texture > y_texture, std::shared_ptr< Texture > uv_texture, YUVColorSpace yuv_color_space)
static sk_sp< DlImageImpeller > Make(std::shared_ptr< Texture > texture, OwningContext owning_context=OwningContext::kIO)
static std::shared_ptr< TextureMTL > Wrapper(TextureDescriptor desc, id< MTLTexture > texture, std::function< void()> deletion_proc=nullptr)
FlutterVulkanImage * image
#define FML_DLOG(severity)
Definition logging.h:121
#define FML_LOG(severity)
Definition logging.h:101
sk_sp< flutter::DlImage > wrapRGBATexture:aiksContext:(nonnull id< MTLTexture > rgbaTex,[aiksContext] nonnull impeller::AiksContext *aiks_context)
sk_sp< flutter::DlImage > wrapYUVATexture:UVTex:YUVColorSpace:aiksContext:(nonnull id< MTLTexture > yTex,[UVTex] nonnull id< MTLTexture > uvTex,[YUVColorSpace] impeller::YUVColorSpace colorSpace,[aiksContext] nonnull impeller::AiksContext *aiksContext)
sk_sp< SkImage > wrapRGBATexture:grContext:width:height:(nonnull id< MTLTexture > rgbaTex,[grContext] nonnull GrDirectContext *grContext,[width] size_t width,[height] size_t height)
sk_sp< SkImage > wrapYUVATexture:UVTex:YUVColorSpace:grContext:width:height:(nonnull id< MTLTexture > yTex,[UVTex] nonnull id< MTLTexture > uvTex,[YUVColorSpace] SkYUVColorSpace colorSpace,[grContext] nonnull GrDirectContext *grContext,[width] size_t width,[height] size_t height)
FlTexture * texture
const DlRect & ToDlRect(const SkRect &rect)
YUVColorSpace
Definition color.h:54
ISize64 ISize
Definition size.h:162
int32_t height
int32_t width
static constexpr std::enable_if_t< std::is_floating_point_v< FT >, TRect > Make(const TRect< U > &rect)
Definition rect.h:157
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
int BOOL