blob: 06af8ac1726845dc178eb7ab446cce4eba047751 [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/testing/test_gl_surface.h"
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <memory>
#include <string>
#include "flutter/fml/logging.h"
#include "flutter/testing/test_gl_utils.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/gpu/ganesh/GrBackendSurface.h"
#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
#include "third_party/skia/include/gpu/ganesh/gl/GrGLAssembleInterface.h"
#include "third_party/skia/include/gpu/ganesh/gl/GrGLBackendSurface.h"
#include "third_party/skia/include/gpu/ganesh/gl/GrGLDirectContext.h"
#include "third_party/skia/include/gpu/ganesh/gl/GrGLTypes.h"
namespace flutter::testing {
TestGLOnscreenOnlySurface::TestGLOnscreenOnlySurface(
std::shared_ptr<TestEGLContext> context,
SkISize size)
: surface_size_(size), egl_context_(std::move(context)) {
const EGLint attributes[] = {
EGL_WIDTH, size.width(), //
EGL_HEIGHT, size.height(), //
EGL_NONE,
};
onscreen_surface_ =
::eglCreatePbufferSurface(egl_context_->display, // display connection
egl_context_->config, // config
attributes // surface attributes
);
FML_CHECK(onscreen_surface_ != EGL_NO_SURFACE) << GetEGLError();
}
TestGLOnscreenOnlySurface::~TestGLOnscreenOnlySurface() {
skia_context_ = nullptr;
auto result = ::eglDestroySurface(egl_context_->display, onscreen_surface_);
FML_CHECK(result == EGL_TRUE) << GetEGLError();
}
const SkISize& TestGLOnscreenOnlySurface::GetSurfaceSize() const {
return surface_size_;
}
bool TestGLOnscreenOnlySurface::MakeCurrent() {
auto result =
::eglMakeCurrent(egl_context_->display, onscreen_surface_,
onscreen_surface_, egl_context_->onscreen_context);
if (result == EGL_FALSE) {
FML_LOG(ERROR) << "Could not make the context current. " << GetEGLError();
}
return result == EGL_TRUE;
}
bool TestGLOnscreenOnlySurface::ClearCurrent() {
auto result = ::eglMakeCurrent(egl_context_->display, EGL_NO_SURFACE,
EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (result == EGL_FALSE) {
FML_LOG(ERROR) << "Could not clear the current context. " << GetEGLError();
}
return result == EGL_TRUE;
}
bool TestGLOnscreenOnlySurface::Present() {
auto result = ::eglSwapBuffers(egl_context_->display, onscreen_surface_);
if (result == EGL_FALSE) {
FML_LOG(ERROR) << "Could not swap buffers. " << GetEGLError();
}
return result == EGL_TRUE;
}
uint32_t TestGLOnscreenOnlySurface::GetFramebuffer(uint32_t width,
uint32_t height) const {
return GetWindowFBOId();
}
void* TestGLOnscreenOnlySurface::GetProcAddress(const char* name) const {
if (name == nullptr) {
return nullptr;
}
auto symbol = ::eglGetProcAddress(name);
if (symbol == NULL) {
FML_LOG(ERROR) << "Could not fetch symbol for name: " << name;
}
return reinterpret_cast<void*>(symbol);
}
sk_sp<GrDirectContext> TestGLOnscreenOnlySurface::GetGrContext() {
if (skia_context_) {
return skia_context_;
}
return CreateGrContext();
}
sk_sp<GrDirectContext> TestGLOnscreenOnlySurface::CreateGrContext() {
if (!MakeCurrent()) {
return nullptr;
}
auto get_string =
reinterpret_cast<PFNGLGETSTRINGPROC>(GetProcAddress("glGetString"));
if (!get_string) {
return nullptr;
}
auto c_version = reinterpret_cast<const char*>(get_string(GL_VERSION));
if (c_version == NULL) {
return nullptr;
}
GrGLGetProc get_proc = [](void* context, const char name[]) -> GrGLFuncPtr {
return reinterpret_cast<GrGLFuncPtr>(
reinterpret_cast<TestGLOnscreenOnlySurface*>(context)->GetProcAddress(
name));
};
std::string version(c_version);
auto interface = version.find("OpenGL ES") == std::string::npos
? GrGLMakeAssembledGLInterface(this, get_proc)
: GrGLMakeAssembledGLESInterface(this, get_proc);
if (!interface) {
return nullptr;
}
skia_context_ = GrDirectContexts::MakeGL(interface);
return skia_context_;
}
sk_sp<SkSurface> TestGLOnscreenOnlySurface::GetOnscreenSurface() {
FML_CHECK(::eglGetCurrentContext() != EGL_NO_CONTEXT);
GrGLFramebufferInfo framebuffer_info = {};
const uint32_t width = surface_size_.width();
const uint32_t height = surface_size_.height();
framebuffer_info.fFBOID = GetFramebuffer(width, height);
#if FML_OS_MACOSX
framebuffer_info.fFormat = 0x8058; // GL_RGBA8
#else
framebuffer_info.fFormat = 0x93A1; // GL_BGRA8;
#endif
auto backend_render_target =
GrBackendRenderTargets::MakeGL(width, // width
height, // height
1, // sample count
8, // stencil bits
framebuffer_info // framebuffer info
);
SkSurfaceProps surface_properties(0, kUnknown_SkPixelGeometry);
auto surface = SkSurfaces::WrapBackendRenderTarget(
GetGrContext().get(), // context
backend_render_target, // backend render target
kBottomLeft_GrSurfaceOrigin, // surface origin
kN32_SkColorType, // color type
SkColorSpace::MakeSRGB(), // color space
&surface_properties, // surface properties
nullptr, // release proc
nullptr // release context
);
if (!surface) {
FML_LOG(ERROR) << "Could not wrap the surface while attempting to "
"snapshot the GL surface.";
return nullptr;
}
return surface;
}
sk_sp<SkImage> TestGLOnscreenOnlySurface::GetRasterSurfaceSnapshot() {
auto surface = GetOnscreenSurface();
if (!surface) {
FML_LOG(ERROR) << "Aborting snapshot because of on-screen surface "
"acquisition failure.";
return nullptr;
}
auto device_snapshot = surface->makeImageSnapshot();
if (!device_snapshot) {
FML_LOG(ERROR) << "Could not create the device snapshot while attempting "
"to snapshot the GL surface.";
return nullptr;
}
auto host_snapshot = device_snapshot->makeRasterImage();
if (!host_snapshot) {
FML_LOG(ERROR) << "Could not create the host snapshot while attempting to "
"snapshot the GL surface.";
return nullptr;
}
return host_snapshot;
}
uint32_t TestGLOnscreenOnlySurface::GetWindowFBOId() const {
return 0u;
}
TestGLSurface::TestGLSurface(SkISize surface_size)
: TestGLSurface(std::make_shared<TestEGLContext>(), surface_size) {}
TestGLSurface::TestGLSurface(std::shared_ptr<TestEGLContext> egl_context,
SkISize surface_size)
: TestGLOnscreenOnlySurface(std::move(egl_context), surface_size) {
{
const EGLint offscreen_surface_attributes[] = {
EGL_WIDTH, 1, //
EGL_HEIGHT, 1, //
EGL_NONE,
};
offscreen_surface_ = ::eglCreatePbufferSurface(
egl_context_->display, // display connection
egl_context_->config, // config
offscreen_surface_attributes // surface attributes
);
FML_CHECK(offscreen_surface_ != EGL_NO_SURFACE) << GetEGLError();
}
}
TestGLSurface::~TestGLSurface() {
auto result = ::eglDestroySurface(egl_context_->display, offscreen_surface_);
FML_CHECK(result == EGL_TRUE) << GetEGLError();
}
bool TestGLSurface::MakeResourceCurrent() {
auto result =
::eglMakeCurrent(egl_context_->display, offscreen_surface_,
offscreen_surface_, egl_context_->offscreen_context);
if (result == EGL_FALSE) {
FML_LOG(ERROR) << "Could not make the resource context current. "
<< GetEGLError();
}
return result == EGL_TRUE;
}
} // namespace flutter::testing