blob: 697992d1b7cd8dc6311918cbd47dbb9fd3b73386 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/darwin/ios/ios_external_texture_gl.h"
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>
#include "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/GrBackendSurface.h"
namespace flutter {
IOSExternalTextureGL::IOSExternalTextureGL(int64_t textureId,
NSObject<FlutterTexture>* externalTexture)
: Texture(textureId),
external_texture_(fml::scoped_nsobject<NSObject<FlutterTexture>>([externalTexture retain])) {
FML_DCHECK(external_texture_);
}
IOSExternalTextureGL::~IOSExternalTextureGL() = default;
void IOSExternalTextureGL::EnsureTextureCacheExists() {
if (!cache_ref_) {
CVOpenGLESTextureCacheRef cache;
CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL,
[EAGLContext currentContext], NULL, &cache);
if (err == noErr) {
cache_ref_.Reset(cache);
} else {
FML_LOG(WARNING) << "Failed to create GLES texture cache: " << err;
return;
}
}
}
void IOSExternalTextureGL::CreateTextureFromPixelBuffer() {
if (buffer_ref_ == nullptr) {
return;
}
CVOpenGLESTextureRef texture;
CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(
kCFAllocatorDefault, cache_ref_, buffer_ref_, nullptr, GL_TEXTURE_2D, GL_RGBA,
static_cast<int>(CVPixelBufferGetWidth(buffer_ref_)),
static_cast<int>(CVPixelBufferGetHeight(buffer_ref_)), GL_BGRA, GL_UNSIGNED_BYTE, 0,
&texture);
if (err != noErr) {
FML_LOG(WARNING) << "Could not create texture from pixel buffer: " << err;
} else {
texture_ref_.Reset(texture);
}
}
bool IOSExternalTextureGL::NeedUpdateTexture(bool freeze) {
// Update texture if `texture_ref_` is reset to `nullptr` when GrContext
// is destroyed or new frame is ready.
return (!freeze && new_frame_ready_) || !texture_ref_;
}
void IOSExternalTextureGL::Paint(SkCanvas& canvas,
const SkRect& bounds,
bool freeze,
GrContext* context) {
EnsureTextureCacheExists();
if (NeedUpdateTexture(freeze)) {
auto pixelBuffer = [external_texture_.get() copyPixelBuffer];
if (pixelBuffer) {
buffer_ref_.Reset(pixelBuffer);
}
CreateTextureFromPixelBuffer();
new_frame_ready_ = false;
}
if (!texture_ref_) {
return;
}
GrGLTextureInfo textureInfo = {CVOpenGLESTextureGetTarget(texture_ref_),
CVOpenGLESTextureGetName(texture_ref_), GL_RGBA8_OES};
GrBackendTexture backendTexture(bounds.width(), bounds.height(), GrMipMapped::kNo, textureInfo);
sk_sp<SkImage> image =
SkImage::MakeFromTexture(context, backendTexture, kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType, kPremul_SkAlphaType, nullptr);
FML_DCHECK(image) << "Failed to create SkImage from Texture.";
if (image) {
canvas.drawImage(image, bounds.x(), bounds.y());
}
}
void IOSExternalTextureGL::OnGrContextCreated() {
// Re-create texture from pixel buffer that was saved before
// OnGrContextDestroyed gets called.
// https://github.com/flutter/flutter/issues/30491
EnsureTextureCacheExists();
CreateTextureFromPixelBuffer();
}
void IOSExternalTextureGL::OnGrContextDestroyed() {
texture_ref_.Reset(nullptr);
cache_ref_.Reset(nullptr);
}
void IOSExternalTextureGL::MarkNewFrameAvailable() {
new_frame_ready_ = true;
}
void IOSExternalTextureGL::OnTextureUnregistered() {
if ([external_texture_ respondsToSelector:@selector(onTextureUnregistered:)]) {
[external_texture_ onTextureUnregistered:external_texture_];
}
}
} // namespace flutter