Flutter Engine
The Flutter Engine
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
5#import "flutter/shell/platform/darwin/graphics/FlutterDarwinExternalTextureMetal.h"
6#include "flutter/display_list/image/dl_image.h"
21
23
25 CVMetalTextureCacheRef _textureCache;
26 NSObject<FlutterTexture>* _externalTexture;
29 CVPixelBufferRef _lastPixelBuffer;
32}
33
34- (instancetype)initWithTextureCache:(nonnull CVMetalTextureCacheRef)textureCache
35 textureID:(int64_t)textureID
36 texture:(NSObject<FlutterTexture>*)texture
37 enableImpeller:(BOOL)enableImpeller {
38 if (self = [super init]) {
39 _textureCache = textureCache;
40 CFRetain(_textureCache);
41 _textureID = textureID;
43 _enableImpeller = enableImpeller;
44 return self;
45 }
46 return nil;
47}
48
49- (void)dealloc {
50 CVPixelBufferRelease(_lastPixelBuffer);
51 if (_textureCache) {
52 CVMetalTextureCacheFlush(_textureCache, // cache
53 0 // options (must be zero)
54 );
55 CFRelease(_textureCache);
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(_externalImage, // image
71 SkRect::Make(_externalImage->bounds()), // source rect
72 bounds, // destination rect
73 sampling, // sampling
74 context.paint, // paint
76 );
77 }
78}
79
80- (void)onNeedsUpdatedTexture:(flutter::Texture::PaintContext&)context {
81 CVPixelBufferRef pixelBuffer = [_externalTexture copyPixelBuffer];
82 if (pixelBuffer) {
83 CVPixelBufferRelease(_lastPixelBuffer);
84 _lastPixelBuffer = pixelBuffer;
85 _pixelFormat = CVPixelBufferGetPixelFormatType(_lastPixelBuffer);
86 }
87
88 // If the application told us there was a texture frame available but did not provide one when
89 // asked for it, reuse the previous texture but make sure to ask again the next time around.
90 sk_sp<flutter::DlImage> image = [self wrapExternalPixelBuffer:_lastPixelBuffer context:context];
91 if (image) {
94 }
95}
96
97- (void)onGrContextCreated {
98 // External images in this backend have no thread affinity and are not tied to the context in any
99 // way. Instead, they are tied to the Metal device which is associated with the cache already and
100 // is consistent throughout the shell run.
101}
102
103- (void)onGrContextDestroyed {
104 // The image must be reset because it is tied to the onscreen context. But the pixel buffer that
105 // created the image is still around. In case of context reacquisition, that last pixel
106 // buffer will be used to materialize the image in case the application fails to provide a new
107 // one.
109 CVMetalTextureCacheFlush(_textureCache, // cache
110 0 // options (must be zero)
111 );
112}
113
114- (void)markNewFrameAvailable {
116}
117
118- (void)onTextureUnregistered {
119 if ([_externalTexture respondsToSelector:@selector(onTextureUnregistered:)]) {
120 [_externalTexture onTextureUnregistered:_externalTexture];
121 }
122}
123
124#pragma mark - External texture skia wrapper methods.
125
126- (sk_sp<flutter::DlImage>)wrapExternalPixelBuffer:(CVPixelBufferRef)pixelBuffer
127 context:(flutter::Texture::PaintContext&)context {
128 if (!pixelBuffer) {
129 return nullptr;
130 }
131
133 if (_pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ||
134 _pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
135 image = [self wrapNV12ExternalPixelBuffer:pixelBuffer context:context];
136 } else if (_pixelFormat == kCVPixelFormatType_32BGRA) {
137 image = [self wrapBGRAExternalPixelBuffer:pixelBuffer context:context];
138 } else {
139 FML_LOG(ERROR) << "Unsupported pixel format: " << _pixelFormat;
140 return nullptr;
141 }
142
143 if (!image) {
144 FML_DLOG(ERROR) << "Could not wrap Metal texture as a display list image.";
145 }
146
147 return image;
148}
149
150- (sk_sp<flutter::DlImage>)wrapNV12ExternalPixelBuffer:(CVPixelBufferRef)pixelBuffer
151 context:(flutter::Texture::PaintContext&)context {
152 SkISize textureSize =
153 SkISize::Make(CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer));
154 CVMetalTextureRef yMetalTexture = nullptr;
155 {
156 CVReturn cvReturn =
157 CVMetalTextureCacheCreateTextureFromImage(/*allocator=*/kCFAllocatorDefault,
158 /*textureCache=*/_textureCache,
159 /*sourceImage=*/pixelBuffer,
160 /*textureAttributes=*/nullptr,
161 /*pixelFormat=*/MTLPixelFormatR8Unorm,
162 /*width=*/textureSize.width(),
163 /*height=*/textureSize.height(),
164 /*planeIndex=*/0u,
165 /*texture=*/&yMetalTexture);
166
167 if (cvReturn != kCVReturnSuccess) {
168 FML_DLOG(ERROR) << "Could not create Metal texture from pixel buffer: CVReturn " << cvReturn;
169 return nullptr;
170 }
171 }
172
173 CVMetalTextureRef uvMetalTexture = nullptr;
174 {
175 CVReturn cvReturn =
176 CVMetalTextureCacheCreateTextureFromImage(/*allocator=*/kCFAllocatorDefault,
177 /*textureCache=*/_textureCache,
178 /*sourceImage=*/pixelBuffer,
179 /*textureAttributes=*/nullptr,
180 /*pixelFormat=*/MTLPixelFormatRG8Unorm,
181 /*width=*/textureSize.width() / 2,
182 /*height=*/textureSize.height() / 2,
183 /*planeIndex=*/1u,
184 /*texture=*/&uvMetalTexture);
185
186 if (cvReturn != kCVReturnSuccess) {
187 FML_DLOG(ERROR) << "Could not create Metal texture from pixel buffer: CVReturn " << cvReturn;
188 return nullptr;
189 }
190 }
191
192 id<MTLTexture> yTex = CVMetalTextureGetTexture(yMetalTexture);
193 CVBufferRelease(yMetalTexture);
194
195 id<MTLTexture> uvTex = CVMetalTextureGetTexture(uvMetalTexture);
196 CVBufferRelease(uvMetalTexture);
197
198 if (_enableImpeller) {
202 yDesc.size = {textureSize.width(), textureSize.height()};
203 yDesc.mip_count = 1;
204 auto yTexture = impeller::TextureMTL::Wrapper(yDesc, yTex);
205 yTexture->SetCoordinateSystem(impeller::TextureCoordinateSystem::kUploadFromHost);
206
210 uvDesc.size = {textureSize.width() / 2, textureSize.height() / 2};
211 uvDesc.mip_count = 1;
212 auto uvTexture = impeller::TextureMTL::Wrapper(uvDesc, uvTex);
213 uvTexture->SetCoordinateSystem(impeller::TextureCoordinateSystem::kUploadFromHost);
214
215 impeller::YUVColorSpace yuvColorSpace =
216 _pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
219
220 return impeller::DlImageImpeller::MakeFromYUVTextures(context.aiks_context, yTexture, uvTexture,
221 yuvColorSpace);
222 }
223
224 SkYUVColorSpace colorSpace = _pixelFormat == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
228 UVTex:uvTex
229 YUVColorSpace:colorSpace
230 grContext:context.gr_context
231 width:textureSize.width()
232 height:textureSize.height()];
233 if (!skImage) {
234 return nullptr;
235 }
236
237 // This image should not escape local use by this flutter::Texture implementation
238 return flutter::DlImage::Make(skImage);
239}
240
241- (sk_sp<flutter::DlImage>)wrapBGRAExternalPixelBuffer:(CVPixelBufferRef)pixelBuffer
242 context:(flutter::Texture::PaintContext&)context {
243 SkISize textureSize =
244 SkISize::Make(CVPixelBufferGetWidth(pixelBuffer), CVPixelBufferGetHeight(pixelBuffer));
245 CVMetalTextureRef metalTexture = nullptr;
246 CVReturn cvReturn =
247 CVMetalTextureCacheCreateTextureFromImage(/*allocator=*/kCFAllocatorDefault,
248 /*textureCache=*/_textureCache,
249 /*sourceImage=*/pixelBuffer,
250 /*textureAttributes=*/nullptr,
251 /*pixelFormat=*/MTLPixelFormatBGRA8Unorm,
252 /*width=*/textureSize.width(),
253 /*height=*/textureSize.height(),
254 /*planeIndex=*/0u,
255 /*texture=*/&metalTexture);
256
257 if (cvReturn != kCVReturnSuccess) {
258 FML_DLOG(ERROR) << "Could not create Metal texture from pixel buffer: CVReturn " << cvReturn;
259 return nullptr;
260 }
261
262 id<MTLTexture> rgbaTex = CVMetalTextureGetTexture(metalTexture);
263 CVBufferRelease(metalTexture);
264
265 if (_enableImpeller) {
269 desc.size = {textureSize.width(), textureSize.height()};
270 desc.mip_count = 1;
274 }
275
277 grContext:context.gr_context
278 width:textureSize.width()
279 height:textureSize.height()];
280 if (!skImage) {
281 return nullptr;
282 }
283
284 // This image should not escape local use by this flutter::Texture implementation
285 return flutter::DlImage::Make(skImage);
286}
287
288@end
289
291
292+ (sk_sp<SkImage>)wrapYUVATexture:(id<MTLTexture>)yTex
293 UVTex:(id<MTLTexture>)uvTex
294 YUVColorSpace:(SkYUVColorSpace)colorSpace
295 grContext:(nonnull GrDirectContext*)grContext
296 width:(size_t)width
297 height:(size_t)height {
298#if SLIMPELLER
299 return nullptr;
300#else // SLIMPELLER
301 GrMtlTextureInfo ySkiaTextureInfo;
302 ySkiaTextureInfo.fTexture = sk_cfp<const void*>{(__bridge_retained const void*)yTex};
303
304 GrBackendTexture skiaBackendTextures[2];
305 skiaBackendTextures[0] =
307
308 GrMtlTextureInfo uvSkiaTextureInfo;
309 uvSkiaTextureInfo.fTexture = sk_cfp<const void*>{(__bridge_retained const void*)uvTex};
310
311 skiaBackendTextures[1] =
313 SkYUVAInfo yuvaInfo(skiaBackendTextures[0].dimensions(), SkYUVAInfo::PlaneConfig::kY_UV,
315 GrYUVABackendTextures yuvaBackendTextures(yuvaInfo, skiaBackendTextures,
317
318 return SkImages::TextureFromYUVATextures(grContext, yuvaBackendTextures,
319 /*imageColorSpace=*/nullptr,
320 /*releaseProc*/ nullptr, /*releaseContext*/ nullptr);
321#endif // SLIMPELLER
322}
323
324+ (sk_sp<SkImage>)wrapRGBATexture:(id<MTLTexture>)rgbaTex
325 grContext:(nonnull GrDirectContext*)grContext
326 width:(size_t)width
327 height:(size_t)height {
328#if SLIMPELLER
329 return nullptr;
330#else // SLIMPELLER
331
332 GrMtlTextureInfo skiaTextureInfo;
333 skiaTextureInfo.fTexture = sk_cfp<const void*>{(__bridge_retained const void*)rgbaTex};
334
335 GrBackendTexture skiaBackendTexture =
337
338 return SkImages::BorrowTextureFrom(grContext, skiaBackendTexture, kTopLeft_GrSurfaceOrigin,
340 /*colorSpace=*/nullptr, /*releaseProc*/ nullptr,
341 /*releaseContext*/ nullptr);
342#endif // SLIMPELLER
343}
344@end
sk_sp< flutter::DlImage > _externalImage
NSObject< FlutterTexture > * _externalTexture
CVPixelBufferRef _lastPixelBuffer
int64_t _textureID
@ kTopLeft_GrSurfaceOrigin
Definition: GrTypes.h:148
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
@ kBGRA_8888_SkColorType
pixel with 8 bits for blue, green, red, alpha; in 32-bit word
Definition: SkColorType.h:26
SkYUVColorSpace
Definition: SkImageInfo.h:68
@ kRec601_Limited_SkYUVColorSpace
describes SDTV range
Definition: SkImageInfo.h:70
@ kJPEG_Full_SkYUVColorSpace
describes full range
Definition: SkImageInfo.h:69
@ kY_UV
Plane 0: Y, Plane 1: UV.
@ k444
No subsampling. UV values for each Y.
SkIRect bounds() const
Definition: dl_image.cc:31
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)
Definition: texture_mtl.mm:40
void reset(T *ptr=nullptr)
Definition: SkRefCnt.h:310
#define FML_DLOG(severity)
Definition: logging.h:102
#define FML_LOG(severity)
Definition: logging.h:82
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
SK_API GrBackendTexture MakeMtl(int width, int height, skgpu::Mipmapped, const GrMtlTextureInfo &mtlInfo, std::string_view label={})
SK_API sk_sp< SkImage > TextureFromYUVATextures(GrRecordingContext *context, const GrYUVABackendTextures &yuvaTextures, sk_sp< SkColorSpace > imageColorSpace, TextureReleaseProc textureReleaseProc=nullptr, ReleaseContext releaseContext=nullptr)
SK_API sk_sp< SkImage > BorrowTextureFrom(GrRecordingContext *context, const GrBackendTexture &backendTexture, GrSurfaceOrigin origin, SkColorType colorType, SkAlphaType alphaType, sk_sp< SkColorSpace > colorSpace, TextureReleaseProc textureReleaseProc=nullptr, ReleaseContext releaseContext=nullptr)
static bool init()
Optional< SkRect > bounds
Definition: SkRecords.h:189
sk_sp< const SkImage > image
Definition: SkRecords.h:269
SkSamplingOptions sampling
Definition: SkRecords.h:337
YUVColorSpace
Definition: color.h:55
int32_t height
int32_t width
Definition: SkSize.h:16
static constexpr SkISize Make(int32_t w, int32_t h)
Definition: SkSize.h:20
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
static SkRect Make(const SkISize &size)
Definition: SkRect.h:669
A lightweight object that describes the attributes of a texture that can then used an allocator to cr...
#define ERROR(message)
Definition: elf_loader.cc:260
int BOOL
Definition: windows_types.h:37