blob: 0b367dd74048214fa1a45aa5d26166690a8dfb80 [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/windows/compositor_opengl.h"
#include "GLES3/gl3.h"
#include "flutter/shell/platform/windows/flutter_windows_engine.h"
#include "flutter/shell/platform/windows/flutter_windows_view.h"
namespace flutter {
namespace {
constexpr uint32_t kWindowFrameBufferId = 0;
// The metadata for an OpenGL framebuffer backing store.
struct FramebufferBackingStore {
uint32_t framebuffer_id;
uint32_t texture_id;
};
// Based off Skia's logic:
// https://github.com/google/skia/blob/4738ed711e03212aceec3cd502a4adb545f38e63/src/gpu/ganesh/gl/GrGLCaps.cpp#L1963-L2116
int GetSupportedTextureFormat(const impeller::DescriptionGLES* description) {
if (description->HasExtension("GL_EXT_texture_format_BGRA8888")) {
return GL_BGRA8_EXT;
} else if (description->HasExtension("GL_APPLE_texture_format_BGRA8888") &&
description->GetGlVersion().IsAtLeast(impeller::Version(3, 0))) {
return GL_BGRA8_EXT;
} else {
return GL_RGBA8;
}
}
} // namespace
CompositorOpenGL::CompositorOpenGL(FlutterWindowsEngine* engine,
impeller::ProcTableGLES::Resolver resolver)
: engine_(engine), resolver_(resolver) {}
bool CompositorOpenGL::CreateBackingStore(
const FlutterBackingStoreConfig& config,
FlutterBackingStore* result) {
if (!is_initialized_ && !Initialize()) {
return false;
}
auto store = std::make_unique<FramebufferBackingStore>();
gl_->GenTextures(1, &store->texture_id);
gl_->GenFramebuffers(1, &store->framebuffer_id);
gl_->BindFramebuffer(GL_FRAMEBUFFER, store->framebuffer_id);
gl_->BindTexture(GL_TEXTURE_2D, store->texture_id);
gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl_->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
gl_->TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, config.size.width,
config.size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
gl_->BindTexture(GL_TEXTURE_2D, 0);
gl_->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, store->texture_id, 0);
result->type = kFlutterBackingStoreTypeOpenGL;
result->open_gl.type = kFlutterOpenGLTargetTypeFramebuffer;
result->open_gl.framebuffer.name = store->framebuffer_id;
result->open_gl.framebuffer.target = format_;
result->open_gl.framebuffer.user_data = store.release();
result->open_gl.framebuffer.destruction_callback = [](void* user_data) {
// Backing store destroyed in `CompositorOpenGL::CollectBackingStore`, set
// on FlutterCompositor.collect_backing_store_callback during engine start.
};
return true;
}
bool CompositorOpenGL::CollectBackingStore(const FlutterBackingStore* store) {
FML_DCHECK(is_initialized_);
FML_DCHECK(store->type == kFlutterBackingStoreTypeOpenGL);
FML_DCHECK(store->open_gl.type == kFlutterOpenGLTargetTypeFramebuffer);
auto user_data = static_cast<FramebufferBackingStore*>(
store->open_gl.framebuffer.user_data);
gl_->DeleteFramebuffers(1, &user_data->framebuffer_id);
gl_->DeleteTextures(1, &user_data->texture_id);
delete user_data;
return true;
}
bool CompositorOpenGL::Present(FlutterWindowsView* view,
const FlutterLayer** layers,
size_t layers_count) {
FML_DCHECK(view != nullptr);
// Clear the view if there are no layers to present.
if (layers_count == 0) {
// Normally the compositor is initialized when the first backing store is
// created. However, on an empty frame no backing stores are created and
// the present needs to initialize the compositor.
if (!is_initialized_ && !Initialize()) {
return false;
}
return Clear(view);
}
// TODO: Support compositing layers and platform views.
// See: https://github.com/flutter/flutter/issues/31713
FML_DCHECK(is_initialized_);
FML_DCHECK(layers_count == 1);
FML_DCHECK(layers[0]->offset.x == 0 && layers[0]->offset.y == 0);
FML_DCHECK(layers[0]->type == kFlutterLayerContentTypeBackingStore);
FML_DCHECK(layers[0]->backing_store->type == kFlutterBackingStoreTypeOpenGL);
FML_DCHECK(layers[0]->backing_store->open_gl.type ==
kFlutterOpenGLTargetTypeFramebuffer);
auto width = layers[0]->size.width;
auto height = layers[0]->size.height;
// Check if this frame can be presented. This resizes the surface if a resize
// is pending and |width| and |height| match the target size.
if (!view->OnFrameGenerated(width, height)) {
return false;
}
// |OnFrameGenerated| should return false if the surface isn't valid.
FML_DCHECK(view->surface() != nullptr);
FML_DCHECK(view->surface()->IsValid());
egl::WindowSurface* surface = view->surface();
if (!surface->MakeCurrent()) {
return false;
}
auto source_id = layers[0]->backing_store->open_gl.framebuffer.name;
// Disable the scissor test as it can affect blit operations.
// Prevents regressions like: https://github.com/flutter/flutter/issues/140828
// See OpenGL specification version 4.6, section 18.3.1.
gl_->Disable(GL_SCISSOR_TEST);
gl_->BindFramebuffer(GL_READ_FRAMEBUFFER, source_id);
gl_->BindFramebuffer(GL_DRAW_FRAMEBUFFER, kWindowFrameBufferId);
gl_->BlitFramebuffer(0, // srcX0
0, // srcY0
width, // srcX1
height, // srcY1
0, // dstX0
0, // dstY0
width, // dstX1
height, // dstY1
GL_COLOR_BUFFER_BIT, // mask
GL_NEAREST // filter
);
if (!surface->SwapBuffers()) {
return false;
}
view->OnFramePresented();
return true;
}
bool CompositorOpenGL::Initialize() {
FML_DCHECK(!is_initialized_);
egl::Manager* manager = engine_->egl_manager();
if (!manager) {
return false;
}
if (!manager->render_context()->MakeCurrent()) {
return false;
}
gl_ = std::make_unique<impeller::ProcTableGLES>(resolver_);
if (!gl_->IsValid()) {
gl_.reset();
return false;
}
format_ = GetSupportedTextureFormat(gl_->GetDescription());
is_initialized_ = true;
return true;
}
bool CompositorOpenGL::Clear(FlutterWindowsView* view) {
FML_DCHECK(is_initialized_);
// Check if this frame can be presented. This resizes the surface if needed.
if (!view->OnEmptyFrameGenerated()) {
return false;
}
// |OnEmptyFrameGenerated| should return false if the surface isn't valid.
FML_DCHECK(view->surface() != nullptr);
FML_DCHECK(view->surface()->IsValid());
egl::WindowSurface* surface = view->surface();
if (!surface->MakeCurrent()) {
return false;
}
gl_->ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl_->Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
if (!surface->SwapBuffers()) {
return false;
}
view->OnFramePresented();
return true;
}
} // namespace flutter