| // 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 |