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