Flutter Engine
ios_external_texture_gl.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/ios/ios_external_texture_gl.h"
6 
7 #import <OpenGLES/EAGL.h>
8 #import <OpenGLES/ES2/gl.h>
9 #import <OpenGLES/ES2/glext.h>
10 
11 #import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
12 #include "third_party/skia/include/core/SkSurface.h"
13 #include "third_party/skia/include/core/SkYUVAInfo.h"
14 #include "third_party/skia/include/gpu/GrBackendSurface.h"
15 #include "third_party/skia/include/gpu/GrDirectContext.h"
16 #include "third_party/skia/include/gpu/GrYUVABackendTextures.h"
17 
18 namespace flutter {
19 
21  NSObject<FlutterTexture>* externalTexture)
22  : Texture(textureId),
23  external_texture_(fml::scoped_nsobject<NSObject<FlutterTexture>>([externalTexture retain])) {
24  FML_DCHECK(external_texture_);
25 }
26 
28 
29 void IOSExternalTextureGL::EnsureTextureCacheExists() {
30  if (!cache_ref_) {
31  CVOpenGLESTextureCacheRef cache;
32  CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL,
33  [EAGLContext currentContext], NULL, &cache);
34  if (err == noErr) {
35  cache_ref_.Reset(cache);
36  } else {
37  FML_LOG(WARNING) << "Failed to create GLES texture cache: " << err;
38  return;
39  }
40  }
41 }
42 
43 void IOSExternalTextureGL::CreateTextureFromPixelBuffer() {
44  if (buffer_ref_ == nullptr) {
45  return;
46  }
47  if (pixel_format_ == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ||
48  pixel_format_ == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
49  CreateYUVTexturesFromPixelBuffer();
50  } else {
51  CreateRGBATextureFromPixelBuffer();
52  }
53 }
54 
55 void IOSExternalTextureGL::CreateRGBATextureFromPixelBuffer() {
56  CVOpenGLESTextureRef texture;
57  CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(
58  kCFAllocatorDefault, cache_ref_, buffer_ref_, /*textureAttributes=*/nullptr, GL_TEXTURE_2D,
59  GL_RGBA, static_cast<int>(CVPixelBufferGetWidth(buffer_ref_)),
60  static_cast<int>(CVPixelBufferGetHeight(buffer_ref_)), GL_BGRA, GL_UNSIGNED_BYTE, 0,
61  &texture);
62  if (err != noErr) {
63  FML_LOG(WARNING) << "Could not create texture from pixel buffer: " << err;
64  } else {
65  texture_ref_.Reset(texture);
66  }
67 }
68 
69 void IOSExternalTextureGL::CreateYUVTexturesFromPixelBuffer() {
70  size_t width = CVPixelBufferGetWidth(buffer_ref_);
71  size_t height = CVPixelBufferGetHeight(buffer_ref_);
72  {
73  CVOpenGLESTextureRef yTexture;
74  CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(
75  kCFAllocatorDefault, cache_ref_, buffer_ref_, /*textureAttributes=*/nullptr, GL_TEXTURE_2D,
76  GL_LUMINANCE, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0, &yTexture);
77  if (err != noErr) {
78  FML_DCHECK(yTexture) << "Could not create texture from pixel buffer: " << err;
79  } else {
80  y_texture_ref_.Reset(yTexture);
81  }
82  }
83 
84  {
85  CVOpenGLESTextureRef uvTexture;
86  CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(
87  kCFAllocatorDefault, cache_ref_, buffer_ref_, /*textureAttributes=*/nullptr, GL_TEXTURE_2D,
88  GL_LUMINANCE_ALPHA, width / 2, height / 2, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 1,
89  &uvTexture);
90  if (err != noErr) {
91  FML_DCHECK(uvTexture) << "Could not create texture from pixel buffer: " << err;
92  } else {
93  uv_texture_ref_.Reset(uvTexture);
94  }
95  }
96 }
97 
98 sk_sp<SkImage> IOSExternalTextureGL::CreateImageFromRGBATexture(GrDirectContext* context,
99  const SkRect& bounds) {
100  GrGLTextureInfo textureInfo = {CVOpenGLESTextureGetTarget(texture_ref_),
101  CVOpenGLESTextureGetName(texture_ref_), GL_RGBA8_OES};
102  GrBackendTexture backendTexture(bounds.width(), bounds.height(), GrMipMapped::kNo, textureInfo);
103  sk_sp<SkImage> image = SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
104  kRGBA_8888_SkColorType, kPremul_SkAlphaType,
105  /*imageColorSpace=*/nullptr);
106  return image;
107 }
108 
109 sk_sp<SkImage> IOSExternalTextureGL::CreateImageFromYUVTextures(GrDirectContext* context,
110  const SkRect& bounds) {
111  GrBackendTexture textures[2];
112  GrGLTextureInfo yTextureInfo = {CVOpenGLESTextureGetTarget(y_texture_ref_),
113  CVOpenGLESTextureGetName(y_texture_ref_), GL_LUMINANCE8_EXT};
114  textures[0] = GrBackendTexture(bounds.width(), bounds.height(), GrMipMapped::kNo, yTextureInfo);
115  GrGLTextureInfo uvTextureInfo = {CVOpenGLESTextureGetTarget(uv_texture_ref_),
116  CVOpenGLESTextureGetName(uv_texture_ref_),
117  GL_LUMINANCE8_ALPHA8_EXT};
118  textures[1] = GrBackendTexture(bounds.width(), bounds.height(), GrMipMapped::kNo, uvTextureInfo);
119 
120  SkYUVAInfo yuvaInfo(textures[0].dimensions(), SkYUVAInfo::PlaneConfig::kY_UV,
121  SkYUVAInfo::Subsampling::k444, kRec601_SkYUVColorSpace);
122  GrYUVABackendTextures yuvaBackendTextures(yuvaInfo, textures, kTopLeft_GrSurfaceOrigin);
123  sk_sp<SkImage> image = SkImage::MakeFromYUVATextures(context, yuvaBackendTextures,
124  /*imageColorSpace=*/nullptr);
125  return image;
126 }
127 
128 bool IOSExternalTextureGL::IsTexturesAvailable() const {
129  return ((pixel_format_ == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ||
130  pixel_format_ == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) &&
131  (y_texture_ref_ && uv_texture_ref_)) ||
132  (pixel_format_ == kCVPixelFormatType_32BGRA && texture_ref_);
133 }
134 
135 bool IOSExternalTextureGL::NeedUpdateTexture(bool freeze) {
136  // Update texture if `texture_ref_` is reset to `nullptr` when GrContext
137  // is destroyed or new frame is ready.
138  return (!freeze && new_frame_ready_) || !IsTexturesAvailable();
139 }
140 
141 void IOSExternalTextureGL::Paint(SkCanvas& canvas,
142  const SkRect& bounds,
143  bool freeze,
144  GrDirectContext* context,
145  const SkSamplingOptions& sampling) {
146  EnsureTextureCacheExists();
147  if (NeedUpdateTexture(freeze)) {
148  auto pixelBuffer = [external_texture_.get() copyPixelBuffer];
149  if (pixelBuffer) {
150  buffer_ref_.Reset(pixelBuffer);
151  pixel_format_ = CVPixelBufferGetPixelFormatType(buffer_ref_);
152  }
153  CreateTextureFromPixelBuffer();
154  new_frame_ready_ = false;
155  }
156  if (!IsTexturesAvailable()) {
157  return;
158  }
159 
160  sk_sp<SkImage> image = nullptr;
161  if (pixel_format_ == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange ||
162  pixel_format_ == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange) {
163  image = CreateImageFromYUVTextures(context, bounds);
164  } else {
165  image = CreateImageFromRGBATexture(context, bounds);
166  }
167 
168  FML_DCHECK(image) << "Failed to create SkImage from Texture.";
169  if (image) {
170  canvas.drawImage(image, bounds.x(), bounds.y(), sampling, nullptr);
171  }
172 }
173 
174 void IOSExternalTextureGL::OnGrContextCreated() {
175  // Re-create texture from pixel buffer that was saved before
176  // OnGrContextDestroyed gets called.
177  // https://github.com/flutter/flutter/issues/30491
178  EnsureTextureCacheExists();
179  CreateTextureFromPixelBuffer();
180 }
181 
182 void IOSExternalTextureGL::OnGrContextDestroyed() {
183  texture_ref_.Reset(nullptr);
184  cache_ref_.Reset(nullptr);
185 }
186 
187 void IOSExternalTextureGL::MarkNewFrameAvailable() {
188  new_frame_ready_ = true;
189 }
190 
191 void IOSExternalTextureGL::OnTextureUnregistered() {
192  if ([external_texture_ respondsToSelector:@selector(onTextureUnregistered:)]) {
193  [external_texture_ onTextureUnregistered:external_texture_];
194  }
195 }
196 
197 } // namespace flutter
G_BEGIN_DECLS FlTexture * texture
#define FML_DCHECK(condition)
Definition: logging.h:86
#define FML_LOG(severity)
Definition: logging.h:65
void Reset(T instance=nullptr)
Definition: cf_utils.h:43
Definition: ascii_trie.cc:9
int32_t width
int32_t height
IOSExternalTextureGL(int64_t textureId, NSObject< FlutterTexture > *externalTexture)