gecko-dev/gfx/gl/GLContextProviderEGL.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1239 строки
37 KiB
C++
Исходник Обычный вид История

/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2012-05-21 15:12:37 +04:00
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#if defined(MOZ_WIDGET_GTK)
# define GET_NATIVE_WINDOW_FROM_REAL_WIDGET(aWidget) \
((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_EGL_WINDOW))
# define GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget) \
(aWidget->AsX11()->GetEGLNativeWindow())
#elif defined(MOZ_WIDGET_ANDROID)
# define GET_NATIVE_WINDOW_FROM_REAL_WIDGET(aWidget) \
((EGLNativeWindowType)aWidget->GetNativeData(NS_JAVA_SURFACE))
# define GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget) \
(aWidget->AsAndroid()->GetEGLNativeWindow())
#elif defined(XP_WIN)
# define GET_NATIVE_WINDOW_FROM_REAL_WIDGET(aWidget) \
((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_WINDOW))
# define GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget) \
((EGLNativeWindowType)aWidget->AsWindows()->GetHwnd())
#else
# define GET_NATIVE_WINDOW_FROM_REAL_WIDGET(aWidget) \
((EGLNativeWindowType)aWidget->GetNativeData(NS_NATIVE_WINDOW))
# define GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget) \
((EGLNativeWindowType)aWidget->RealWidget()->GetNativeData( \
NS_NATIVE_WINDOW))
#endif
#if defined(XP_UNIX)
# ifdef MOZ_WIDGET_ANDROID
# include <android/native_window.h>
# include <android/native_window_jni.h>
# include "mozilla/widget/AndroidCompositorWidget.h"
# endif
# define GLES2_LIB "libGLESv2.so"
# define GLES2_LIB2 "libGLESv2.so.2"
#elif defined(XP_WIN)
# include "mozilla/widget/WinCompositorWidget.h"
# include "nsIFile.h"
# define GLES2_LIB "libGLESv2.dll"
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN 1
# endif
# include <windows.h>
#else
# error "Platform not recognized"
#endif
#include "gfxASurface.h"
#include "gfxCrashReporterUtils.h"
#include "gfxFailure.h"
#include "gfxPlatform.h"
#include "gfxUtils.h"
#include "GLBlitHelper.h"
#include "GLContextEGL.h"
#include "GLContextProvider.h"
#include "GLLibraryEGL.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/BuildConstants.h"
#include "mozilla/layers/CompositorOptions.h"
#include "mozilla/widget/CompositorWidget.h"
#include "nsDebug.h"
#include "nsIWidget.h"
#include "nsThreadUtils.h"
#include "ScopedGLHelpers.h"
#include "TextureImageEGL.h"
#if defined(MOZ_WIDGET_GTK)
# include "mozilla/widget/GtkCompositorWidget.h"
# if defined(MOZ_WAYLAND)
# include <dlfcn.h>
# include <gdk/gdkwayland.h>
# include <wayland-egl.h>
# define MOZ_GTK_WAYLAND 1
# endif
#endif
inline bool IsWaylandDisplay() {
#ifdef MOZ_GTK_WAYLAND
return gdk_display_get_default() &&
GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default());
#else
return false;
#endif
}
inline bool IsX11Display() {
#ifdef MOZ_WIDGET_GTK
return gdk_display_get_default() &&
GDK_IS_X11_DISPLAY(gdk_display_get_default());
#else
return false;
#endif
}
struct wl_egl_window;
using namespace mozilla::gfx;
namespace mozilla {
namespace gl {
using namespace mozilla::widget;
#if defined(MOZ_WAYLAND)
class WaylandGLSurface {
public:
WaylandGLSurface(struct wl_surface* aWaylandSurface,
struct wl_egl_window* aEGLWindow);
~WaylandGLSurface();
private:
struct wl_surface* mWaylandSurface;
struct wl_egl_window* mEGLWindow;
};
static nsDataHashtable<nsPtrHashKey<void>, WaylandGLSurface*> sWaylandGLSurface;
void DeleteWaylandGLSurface(EGLSurface surface) {
// We're running on Wayland which means our EGLSurface may
// have attached Wayland backend data which must be released.
if (IsWaylandDisplay()) {
auto entry = sWaylandGLSurface.Lookup(surface);
if (entry) {
delete entry.Data();
entry.Remove();
}
}
}
#endif
static bool CreateConfigScreen(EglDisplay&, EGLConfig* const aConfig,
const bool aEnableDepthBuffer,
const bool aUseGles, int aVisual = 0);
// append three zeros at the end of attribs list to work around
// EGL implementation bugs that iterate until they find 0, instead of
// EGL_NONE. See bug 948406.
#define EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS \
LOCAL_EGL_NONE, 0, 0, 0
static EGLint kTerminationAttribs[] = {
EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS};
static int next_power_of_two(int v) {
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
static bool is_power_of_two(int v) {
NS_ASSERTION(v >= 0, "bad value");
if (v == 0) return true;
return (v & (v - 1)) == 0;
}
static void DestroySurface(EglDisplay& egl, const EGLSurface oldSurface) {
if (oldSurface != EGL_NO_SURFACE) {
// TODO: This breaks TLS MakeCurrent caching.
egl.fMakeCurrent(EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
egl.fDestroySurface(oldSurface);
#if defined(MOZ_WAYLAND)
DeleteWaylandGLSurface(oldSurface);
#endif
}
}
static EGLSurface CreateFallbackSurface(EglDisplay& egl,
const EGLConfig& config) {
if (egl.IsExtensionSupported(EGLExtension::KHR_surfaceless_context)) {
// We don't need a PBuffer surface in this case
return EGL_NO_SURFACE;
}
std::vector<EGLint> pbattrs;
pbattrs.push_back(LOCAL_EGL_WIDTH);
pbattrs.push_back(1);
pbattrs.push_back(LOCAL_EGL_HEIGHT);
pbattrs.push_back(1);
for (const auto& cur : kTerminationAttribs) {
pbattrs.push_back(cur);
}
EGLSurface surface = egl.fCreatePbufferSurface(config, pbattrs.data());
if (!surface) {
MOZ_CRASH("Failed to create fallback EGLSurface");
}
return surface;
}
static EGLSurface CreateSurfaceFromNativeWindow(
EglDisplay& egl, const EGLNativeWindowType window, const EGLConfig config) {
MOZ_ASSERT(window);
EGLSurface newSurface = EGL_NO_SURFACE;
#ifdef MOZ_WIDGET_ANDROID
JNIEnv* const env = jni::GetEnvForThread();
ANativeWindow* const nativeWindow =
ANativeWindow_fromSurface(env, reinterpret_cast<jobject>(window));
if (!nativeWindow) {
return EGL_NO_SURFACE;
}
const auto& display = egl.mLib->fGetDisplay(EGL_DEFAULT_DISPLAY);
newSurface = egl.mLib->fCreateWindowSurface(display, config, nativeWindow, 0);
ANativeWindow_release(nativeWindow);
#else
newSurface = egl.fCreateWindowSurface(config, window, 0);
if (!newSurface) {
const auto err = egl.mLib->fGetError();
gfxCriticalNote << "Failed to create EGLSurface!: " << gfx::hexa(err);
}
#endif
return newSurface;
}
/* GLContextEGLFactory class was added as a friend of GLContextEGL
* so that it could access GLContextEGL::CreateGLContext. This was
* done so that a new function would not need to be added to the shared
* GLContextProvider interface.
*/
class GLContextEGLFactory {
public:
static already_AddRefed<GLContext> Create(EGLNativeWindowType aWindow,
bool aWebRender, int32_t aDepth);
static already_AddRefed<GLContext> CreateImpl(EGLNativeWindowType aWindow,
bool aWebRender, bool aUseGles,
int32_t aDepth);
private:
GLContextEGLFactory() = default;
~GLContextEGLFactory() = default;
};
already_AddRefed<GLContext> GLContextEGLFactory::CreateImpl(
EGLNativeWindowType aWindow, bool aWebRender, bool aUseGles,
int32_t aDepth) {
nsCString failureId;
const auto lib = gl::DefaultEglLibrary(&failureId);
if (!lib) {
gfxCriticalNote << "Failed[3] to load EGL library: " << failureId.get();
return nullptr;
}
const auto egl = lib->CreateDisplay(true, &failureId);
if (!egl) {
gfxCriticalNote << "Failed[3] to create EGL library display: "
<< failureId.get();
return nullptr;
}
int visualID = 0;
if (IsX11Display()) {
#ifdef MOZ_X11
GdkDisplay* gdkDisplay = gdk_display_get_default();
auto display = gdkDisplay ? GDK_DISPLAY_XDISPLAY(gdkDisplay) : nullptr;
if (display) {
XWindowAttributes windowAttrs;
if (!XGetWindowAttributes(display, (Window)aWindow, &windowAttrs)) {
NS_WARNING("[EGL] XGetWindowAttributes() failed");
return nullptr;
}
visualID = XVisualIDFromVisual(windowAttrs.visual);
}
#endif
}
bool doubleBuffered = true;
EGLConfig config;
if (aWebRender && egl->mLib->IsANGLE()) {
// Force enable alpha channel to make sure ANGLE use correct framebuffer
// formart
const int bpp = 32;
const bool withDepth = true;
if (!CreateConfig(*egl, &config, bpp, withDepth, aUseGles)) {
gfxCriticalNote << "Failed to create EGLConfig for WebRender ANGLE!";
return nullptr;
}
} else {
if (aDepth) {
if (!CreateConfig(*egl, &config, aDepth, aWebRender, aUseGles,
visualID)) {
gfxCriticalNote
<< "Failed to create EGLConfig for WebRender with depth!";
return nullptr;
}
} else {
if (!CreateConfigScreen(*egl, &config,
/* aEnableDepthBuffer */ aWebRender, aUseGles,
visualID)) {
gfxCriticalNote << "Failed to create EGLConfig!";
return nullptr;
}
}
}
EGLSurface surface = EGL_NO_SURFACE;
if (aWindow) {
surface = mozilla::gl::CreateSurfaceFromNativeWindow(*egl, aWindow, config);
if (!surface) {
return nullptr;
}
}
CreateContextFlags flags = CreateContextFlags::NONE;
if (aWebRender && aUseGles) {
flags |= CreateContextFlags::PREFER_ES3;
}
if (!aWebRender) {
flags |= CreateContextFlags::REQUIRE_COMPAT_PROFILE;
}
const auto desc = GLContextDesc{{flags}, false};
RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(
egl, desc, config, surface, aUseGles, &failureId);
if (!gl) {
const auto err = egl->mLib->fGetError();
2019-03-30 09:41:48 +03:00
gfxCriticalNote << "Failed to create EGLContext!: " << gfx::hexa(err);
mozilla::gl::DestroySurface(*egl, surface);
return nullptr;
}
gl->MakeCurrent();
gl->SetIsDoubleBuffered(doubleBuffered);
if (surface && IsWaylandDisplay()) {
// Make eglSwapBuffers() non-blocking on wayland
egl->fSwapInterval(0);
}
if (aWebRender && egl->mLib->IsANGLE()) {
MOZ_ASSERT(doubleBuffered);
egl->fSwapInterval(0);
}
return gl.forget();
}
already_AddRefed<GLContext> GLContextEGLFactory::Create(
EGLNativeWindowType aWindow, bool aWebRender, int32_t aDepth) {
RefPtr<GLContext> glContext;
#if !defined(MOZ_WIDGET_ANDROID)
glContext = CreateImpl(aWindow, aWebRender, /* aUseGles */ false, aDepth);
#endif // !defined(MOZ_WIDGET_ANDROID)
if (!glContext) {
glContext = CreateImpl(aWindow, aWebRender, /* aUseGles */ true, aDepth);
}
return glContext.forget();
}
/* static */
EGLSurface GLContextEGL::CreateEGLSurfaceForCompositorWidget(
widget::CompositorWidget* aCompositorWidget, const EGLConfig aConfig) {
nsCString discardFailureId;
const auto egl = DefaultEglDisplay(&discardFailureId);
if (!egl) {
gfxCriticalNote << "Failed to load EGL library 6!";
return EGL_NO_SURFACE;
}
MOZ_ASSERT(aCompositorWidget);
EGLNativeWindowType window =
GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget);
if (!window) {
gfxCriticalNote << "window is null";
return EGL_NO_SURFACE;
}
return mozilla::gl::CreateSurfaceFromNativeWindow(*egl, window, aConfig);
}
GLContextEGL::GLContextEGL(const std::shared_ptr<EglDisplay> egl,
const GLContextDesc& desc, EGLConfig config,
EGLSurface surface, EGLContext context)
: GLContext(desc, nullptr, false),
mEgl(egl),
mConfig(config),
mContext(context),
mSurface(surface),
mFallbackSurface(CreateFallbackSurface(*mEgl, mConfig)) {
#ifdef DEBUG
printf_stderr("Initializing context %p surface %p on display %p\n", mContext,
mSurface, mEgl->mDisplay);
#endif
}
void GLContextEGL::OnMarkDestroyed() {
if (mSurfaceOverride != EGL_NO_SURFACE) {
SetEGLSurfaceOverride(EGL_NO_SURFACE);
}
}
GLContextEGL::~GLContextEGL() {
MarkDestroyed();
// Wrapped context should not destroy eglContext/Surface
if (!mOwnsContext) {
return;
}
#ifdef DEBUG
printf_stderr("Destroying context %p surface %p on display %p\n", mContext,
mSurface, mEgl->mDisplay);
#endif
mEgl->fDestroyContext(mContext);
mozilla::gl::DestroySurface(*mEgl, mSurface);
mozilla::gl::DestroySurface(*mEgl, mFallbackSurface);
}
bool GLContextEGL::Init() {
if (!GLContext::Init()) return false;
bool current = MakeCurrent();
if (!current) {
gfx::LogFailure("Couldn't get device attachments for device."_ns);
return false;
}
mShareWithEGLImage =
mEgl->HasKHRImageBase() &&
mEgl->IsExtensionSupported(EGLExtension::KHR_gl_texture_2D_image) &&
IsExtensionSupported(OES_EGL_image);
return true;
}
bool GLContextEGL::BindTexImage() {
if (!mSurface) return false;
if (mBound && !ReleaseTexImage()) return false;
EGLBoolean success =
mEgl->fBindTexImage((EGLSurface)mSurface, LOCAL_EGL_BACK_BUFFER);
if (success == LOCAL_EGL_FALSE) return false;
mBound = true;
return true;
}
bool GLContextEGL::ReleaseTexImage() {
if (!mBound) return true;
if (!mSurface) return false;
EGLBoolean success;
success = mEgl->fReleaseTexImage((EGLSurface)mSurface, LOCAL_EGL_BACK_BUFFER);
if (success == LOCAL_EGL_FALSE) return false;
mBound = false;
return true;
}
void GLContextEGL::SetEGLSurfaceOverride(EGLSurface surf) {
mSurfaceOverride = surf;
DebugOnly<bool> ok = MakeCurrent(true);
MOZ_ASSERT(ok);
}
bool GLContextEGL::MakeCurrentImpl() const {
EGLSurface surface =
(mSurfaceOverride != EGL_NO_SURFACE) ? mSurfaceOverride : mSurface;
if (!surface) {
surface = mFallbackSurface;
}
const bool succeeded = mEgl->fMakeCurrent(surface, surface, mContext);
if (!succeeded) {
const auto eglError = mEgl->mLib->fGetError();
if (eglError == LOCAL_EGL_CONTEXT_LOST) {
OnContextLostError();
} else {
NS_WARNING("Failed to make GL context current!");
#ifdef DEBUG
printf_stderr("EGL Error: 0x%04x\n", eglError);
#endif
}
}
return succeeded;
}
bool GLContextEGL::IsCurrentImpl() const {
return mEgl->mLib->fGetCurrentContext() == mContext;
}
bool GLContextEGL::RenewSurface(CompositorWidget* aWidget) {
if (!mOwnsContext) {
return false;
}
// unconditionally release the surface and create a new one. Don't try to
// optimize this away. If we get here, then by definition we know that we want
// to get a new surface.
ReleaseSurface();
MOZ_ASSERT(aWidget);
EGLNativeWindowType nativeWindow =
GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aWidget);
if (nativeWindow) {
mSurface = mozilla::gl::CreateSurfaceFromNativeWindow(*mEgl, nativeWindow,
mConfig);
if (!mSurface) {
NS_WARNING("Failed to create EGLSurface from native window");
return false;
}
}
const bool ok = MakeCurrent(true);
MOZ_ASSERT(ok);
if (mSurface && IsWaylandDisplay()) {
// Make eglSwapBuffers() non-blocking on wayland
mEgl->fSwapInterval(0);
}
return ok;
}
void GLContextEGL::ReleaseSurface() {
if (mOwnsContext) {
mozilla::gl::DestroySurface(*mEgl, mSurface);
}
if (mSurface == mSurfaceOverride) {
mSurfaceOverride = EGL_NO_SURFACE;
}
mSurface = EGL_NO_SURFACE;
}
Maybe<SymbolLoader> GLContextEGL::GetSymbolLoader() const {
return mEgl->mLib->GetSymbolLoader();
}
bool GLContextEGL::SwapBuffers() {
EGLSurface surface =
mSurfaceOverride != EGL_NO_SURFACE ? mSurfaceOverride : mSurface;
if (surface) {
if ((mEgl->IsExtensionSupported(
EGLExtension::EXT_swap_buffers_with_damage) ||
mEgl->IsExtensionSupported(
EGLExtension::KHR_swap_buffers_with_damage))) {
std::vector<EGLint> rects;
for (auto iter = mDamageRegion.RectIter(); !iter.Done(); iter.Next()) {
const IntRect& r = iter.Get();
rects.push_back(r.X());
rects.push_back(r.Y());
rects.push_back(r.Width());
rects.push_back(r.Height());
}
mDamageRegion.SetEmpty();
return mEgl->fSwapBuffersWithDamage(surface, rects.data(),
rects.size() / 4);
}
return mEgl->fSwapBuffers(surface);
} else {
return false;
}
}
void GLContextEGL::SetDamage(const nsIntRegion& aDamageRegion) {
mDamageRegion = aDamageRegion;
}
void GLContextEGL::GetWSIInfo(nsCString* const out) const {
out->AppendLiteral("EGL_VENDOR: ");
out->Append(
(const char*)mEgl->mLib->fQueryString(mEgl->mDisplay, LOCAL_EGL_VENDOR));
out->AppendLiteral("\nEGL_VERSION: ");
out->Append(
(const char*)mEgl->mLib->fQueryString(mEgl->mDisplay, LOCAL_EGL_VERSION));
out->AppendLiteral("\nEGL_EXTENSIONS: ");
out->Append((const char*)mEgl->mLib->fQueryString(mEgl->mDisplay,
LOCAL_EGL_EXTENSIONS));
#ifndef ANDROID // This query will crash some old android.
out->AppendLiteral("\nEGL_EXTENSIONS(nullptr): ");
out->Append(
(const char*)mEgl->mLib->fQueryString(nullptr, LOCAL_EGL_EXTENSIONS));
#endif
}
// hold a reference to the given surface
// for the lifetime of this context.
void GLContextEGL::HoldSurface(gfxASurface* aSurf) { mThebesSurface = aSurf; }
Bug 1575765 - Implement KHR_partial_update for webrender. r=sotaro,jgilbert KHR_partial_update allows us to avoid rerendering the entire backbuffer every frame, and instead only render what has changed on the current frame, as well as the difference between the current backbuffer and the current frontbuffer. It works similarily to EXT_buffer_age, which we already support, with the additional requirement that we must call eglSetDamageRegion each frame before rendering to the backbuffer. Modify GLContextEGL::GetBufferAge() so that it queries the age if either EXT_buffer_age or KHR_partial_update are available. This will now automatically be queried by webrender through the PartialPresentCompositor trait. Add a new function to that trait, set_buffer_damage_region(), whose RenderCompositorEGL implementation calls eglSetDamageRegion(). Call this from composite_simple(), once the damage rect has been calculated but before rendering to the backbuffer. Additionally, change both RenderCompositorEGL and RenderCompositorOGL's implementations of ShouldDrawPreviousPartialPresentRegions() to unconditionally return true, rather than checking for the existence of EXT_buffer_age (or adding a new check for KHR_partial_update). The lack of these extensions does not mean that webrender is able to skip rendering previous frames' damage. Rather the opposite, it means we cannot render *only* the previous frames' damage, and must instead always render the entire buffer. Differential Revision: https://phabricator.services.mozilla.com/D91203
2020-10-02 13:23:56 +03:00
bool GLContextEGL::HasExtBufferAge() const {
return mEgl->IsExtensionSupported(EGLExtension::EXT_buffer_age);
}
Bug 1575765 - Implement KHR_partial_update for webrender. r=sotaro,jgilbert KHR_partial_update allows us to avoid rerendering the entire backbuffer every frame, and instead only render what has changed on the current frame, as well as the difference between the current backbuffer and the current frontbuffer. It works similarily to EXT_buffer_age, which we already support, with the additional requirement that we must call eglSetDamageRegion each frame before rendering to the backbuffer. Modify GLContextEGL::GetBufferAge() so that it queries the age if either EXT_buffer_age or KHR_partial_update are available. This will now automatically be queried by webrender through the PartialPresentCompositor trait. Add a new function to that trait, set_buffer_damage_region(), whose RenderCompositorEGL implementation calls eglSetDamageRegion(). Call this from composite_simple(), once the damage rect has been calculated but before rendering to the backbuffer. Additionally, change both RenderCompositorEGL and RenderCompositorOGL's implementations of ShouldDrawPreviousPartialPresentRegions() to unconditionally return true, rather than checking for the existence of EXT_buffer_age (or adding a new check for KHR_partial_update). The lack of these extensions does not mean that webrender is able to skip rendering previous frames' damage. Rather the opposite, it means we cannot render *only* the previous frames' damage, and must instead always render the entire buffer. Differential Revision: https://phabricator.services.mozilla.com/D91203
2020-10-02 13:23:56 +03:00
bool GLContextEGL::HasKhrPartialUpdate() const {
return mEgl->IsExtensionSupported(EGLExtension::KHR_partial_update);
}
GLint GLContextEGL::GetBufferAge() const {
EGLSurface surface =
mSurfaceOverride != EGL_NO_SURFACE ? mSurfaceOverride : mSurface;
Bug 1575765 - Implement KHR_partial_update for webrender. r=sotaro,jgilbert KHR_partial_update allows us to avoid rerendering the entire backbuffer every frame, and instead only render what has changed on the current frame, as well as the difference between the current backbuffer and the current frontbuffer. It works similarily to EXT_buffer_age, which we already support, with the additional requirement that we must call eglSetDamageRegion each frame before rendering to the backbuffer. Modify GLContextEGL::GetBufferAge() so that it queries the age if either EXT_buffer_age or KHR_partial_update are available. This will now automatically be queried by webrender through the PartialPresentCompositor trait. Add a new function to that trait, set_buffer_damage_region(), whose RenderCompositorEGL implementation calls eglSetDamageRegion(). Call this from composite_simple(), once the damage rect has been calculated but before rendering to the backbuffer. Additionally, change both RenderCompositorEGL and RenderCompositorOGL's implementations of ShouldDrawPreviousPartialPresentRegions() to unconditionally return true, rather than checking for the existence of EXT_buffer_age (or adding a new check for KHR_partial_update). The lack of these extensions does not mean that webrender is able to skip rendering previous frames' damage. Rather the opposite, it means we cannot render *only* the previous frames' damage, and must instead always render the entire buffer. Differential Revision: https://phabricator.services.mozilla.com/D91203
2020-10-02 13:23:56 +03:00
if (surface && (HasExtBufferAge() || HasKhrPartialUpdate())) {
EGLint result;
mEgl->fQuerySurface(surface, LOCAL_EGL_BUFFER_AGE_EXT, &result);
return result;
}
return 0;
}
#define LOCAL_EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ 0x6000
RefPtr<GLContextEGL> GLContextEGL::CreateGLContext(
const std::shared_ptr<EglDisplay> egl, const GLContextDesc& desc,
EGLConfig config, EGLSurface surface, const bool useGles,
nsACString* const out_failureId) {
const auto& flags = desc.flags;
std::vector<EGLint> required_attribs;
if (useGles) {
// TODO: This fBindAPI could be more thread-safe
if (egl->mLib->fBindAPI(LOCAL_EGL_OPENGL_ES_API) == LOCAL_EGL_FALSE) {
*out_failureId = "FEATURE_FAILURE_EGL_ES"_ns;
NS_WARNING("Failed to bind API to GLES!");
return nullptr;
}
required_attribs.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION);
if (flags & CreateContextFlags::PREFER_ES3) {
required_attribs.push_back(3);
} else {
required_attribs.push_back(2);
}
} else {
if (egl->mLib->fBindAPI(LOCAL_EGL_OPENGL_API) == LOCAL_EGL_FALSE) {
*out_failureId = "FEATURE_FAILURE_EGL"_ns;
NS_WARNING("Failed to bind API to GL!");
return nullptr;
}
if (flags & CreateContextFlags::REQUIRE_COMPAT_PROFILE) {
required_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_PROFILE_MASK);
required_attribs.push_back(
LOCAL_EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
required_attribs.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION);
required_attribs.push_back(2);
} else {
// !REQUIRE_COMPAT_PROFILE means core profle.
required_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_PROFILE_MASK);
required_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT);
required_attribs.push_back(LOCAL_EGL_CONTEXT_MAJOR_VERSION);
required_attribs.push_back(3);
required_attribs.push_back(LOCAL_EGL_CONTEXT_MINOR_VERSION);
required_attribs.push_back(2);
}
}
if ((flags & CreateContextFlags::PREFER_EXACT_VERSION) &&
egl->mLib->IsANGLE()) {
required_attribs.push_back(
LOCAL_EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE);
required_attribs.push_back(LOCAL_EGL_FALSE);
}
const auto debugFlags = GLContext::ChooseDebugFlags(flags);
if (!debugFlags && flags & CreateContextFlags::NO_VALIDATION &&
egl->IsExtensionSupported(EGLExtension::KHR_create_context_no_error)) {
required_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_NO_ERROR_KHR);
required_attribs.push_back(LOCAL_EGL_TRUE);
}
if (flags & CreateContextFlags::PROVOKING_VERTEX_DONT_CARE &&
egl->IsExtensionSupported(
EGLExtension::MOZ_create_context_provoking_vertex_dont_care)) {
required_attribs.push_back(
LOCAL_EGL_CONTEXT_PROVOKING_VERTEX_DONT_CARE_MOZ);
required_attribs.push_back(LOCAL_EGL_TRUE);
}
std::vector<EGLint> robustness_attribs;
std::vector<EGLint> rbab_attribs; // RBAB: Robust Buffer Access Behavior
if (flags & CreateContextFlags::PREFER_ROBUSTNESS) {
if (egl->IsExtensionSupported(
EGLExtension::EXT_create_context_robustness)) {
robustness_attribs = required_attribs;
robustness_attribs.push_back(
LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
robustness_attribs.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_EXT);
if (gfxVars::AllowEglRbab()) {
rbab_attribs = robustness_attribs;
rbab_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT);
rbab_attribs.push_back(LOCAL_EGL_TRUE);
}
} else if (egl->IsExtensionSupported(EGLExtension::KHR_create_context)) {
robustness_attribs = required_attribs;
robustness_attribs.push_back(
LOCAL_EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR);
robustness_attribs.push_back(LOCAL_EGL_LOSE_CONTEXT_ON_RESET_KHR);
rbab_attribs = robustness_attribs;
rbab_attribs.push_back(LOCAL_EGL_CONTEXT_FLAGS_KHR);
rbab_attribs.push_back(LOCAL_EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR);
}
}
const auto fnCreate = [&](const std::vector<EGLint>& attribs) {
auto terminated_attribs = attribs;
for (const auto& cur : kTerminationAttribs) {
terminated_attribs.push_back(cur);
}
return egl->fCreateContext(config, EGL_NO_CONTEXT,
terminated_attribs.data());
};
EGLContext context;
do {
if (!rbab_attribs.empty()) {
context = fnCreate(rbab_attribs);
if (context) break;
NS_WARNING("Failed to create EGLContext with rbab_attribs");
}
if (!robustness_attribs.empty()) {
context = fnCreate(robustness_attribs);
if (context) break;
NS_WARNING("Failed to create EGLContext with robustness_attribs");
}
context = fnCreate(required_attribs);
if (context) break;
NS_WARNING("Failed to create EGLContext with required_attribs");
*out_failureId = "FEATURE_FAILURE_EGL_CREATE"_ns;
return nullptr;
} while (false);
MOZ_ASSERT(context);
RefPtr<GLContextEGL> glContext =
new GLContextEGL(egl, desc, config, surface, context);
if (!glContext->Init()) {
*out_failureId = "FEATURE_FAILURE_EGL_INIT"_ns;
return nullptr;
}
if (GLContext::ShouldSpew()) {
printf_stderr("new GLContextEGL %p on EGLDisplay %p\n", glContext.get(),
egl->mDisplay);
}
return glContext;
}
// static
EGLSurface GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(
EglDisplay& egl, EGLConfig config, EGLenum bindToTextureFormat,
mozilla::gfx::IntSize& pbsize) {
nsTArray<EGLint> pbattrs(16);
EGLSurface surface = nullptr;
TRY_AGAIN_POWER_OF_TWO:
pbattrs.Clear();
pbattrs.AppendElement(LOCAL_EGL_WIDTH);
pbattrs.AppendElement(pbsize.width);
pbattrs.AppendElement(LOCAL_EGL_HEIGHT);
pbattrs.AppendElement(pbsize.height);
if (bindToTextureFormat != LOCAL_EGL_NONE) {
pbattrs.AppendElement(LOCAL_EGL_TEXTURE_TARGET);
pbattrs.AppendElement(LOCAL_EGL_TEXTURE_2D);
pbattrs.AppendElement(LOCAL_EGL_TEXTURE_FORMAT);
pbattrs.AppendElement(bindToTextureFormat);
}
for (const auto& cur : kTerminationAttribs) {
pbattrs.AppendElement(cur);
}
surface = egl.fCreatePbufferSurface(config, &pbattrs[0]);
if (!surface) {
if (!is_power_of_two(pbsize.width) || !is_power_of_two(pbsize.height)) {
if (!is_power_of_two(pbsize.width))
pbsize.width = next_power_of_two(pbsize.width);
if (!is_power_of_two(pbsize.height))
pbsize.height = next_power_of_two(pbsize.height);
NS_WARNING("Failed to create pbuffer, trying power of two dims");
goto TRY_AGAIN_POWER_OF_TWO;
}
NS_WARNING("Failed to create pbuffer surface");
return nullptr;
}
return surface;
}
#if defined(MOZ_WAYLAND)
WaylandGLSurface::WaylandGLSurface(struct wl_surface* aWaylandSurface,
struct wl_egl_window* aEGLWindow)
: mWaylandSurface(aWaylandSurface), mEGLWindow(aEGLWindow) {}
WaylandGLSurface::~WaylandGLSurface() {
wl_egl_window_destroy(mEGLWindow);
wl_surface_destroy(mWaylandSurface);
}
#endif
// static
EGLSurface GLContextEGL::CreateWaylandBufferSurface(
EglDisplay& egl, EGLConfig config, mozilla::gfx::IntSize& pbsize) {
wl_egl_window* eglwindow = nullptr;
#ifdef MOZ_GTK_WAYLAND
struct wl_compositor* compositor =
gdk_wayland_display_get_wl_compositor(gdk_display_get_default());
struct wl_surface* wlsurface = wl_compositor_create_surface(compositor);
eglwindow = wl_egl_window_create(wlsurface, pbsize.width, pbsize.height);
#endif
if (!eglwindow) return nullptr;
const auto surface = egl.fCreateWindowSurface(
config, reinterpret_cast<EGLNativeWindowType>(eglwindow), 0);
if (surface) {
#ifdef MOZ_GTK_WAYLAND
WaylandGLSurface* waylandData = new WaylandGLSurface(wlsurface, eglwindow);
auto entry = sWaylandGLSurface.LookupForAdd(surface);
entry.OrInsert([&waylandData]() { return waylandData; });
#endif
}
return surface;
}
static const EGLint kEGLConfigAttribsRGB16[] = {
LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
LOCAL_EGL_RED_SIZE, 5,
LOCAL_EGL_GREEN_SIZE, 6,
LOCAL_EGL_BLUE_SIZE, 5,
LOCAL_EGL_ALPHA_SIZE, 0};
static const EGLint kEGLConfigAttribsRGB24[] = {
LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
LOCAL_EGL_RED_SIZE, 8,
LOCAL_EGL_GREEN_SIZE, 8,
LOCAL_EGL_BLUE_SIZE, 8,
LOCAL_EGL_ALPHA_SIZE, 0};
static const EGLint kEGLConfigAttribsRGBA32[] = {
LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
LOCAL_EGL_RED_SIZE, 8,
LOCAL_EGL_GREEN_SIZE, 8,
LOCAL_EGL_BLUE_SIZE, 8,
LOCAL_EGL_ALPHA_SIZE, 8};
bool CreateConfig(EglDisplay& egl, EGLConfig* aConfig, int32_t depth,
bool aEnableDepthBuffer, bool aUseGles, int aVisual) {
EGLConfig configs[64];
std::vector<EGLint> attribs;
EGLint ncfg = ArrayLength(configs);
switch (depth) {
case 16:
for (const auto& cur : kEGLConfigAttribsRGB16) {
attribs.push_back(cur);
}
break;
case 24:
for (const auto& cur : kEGLConfigAttribsRGB24) {
attribs.push_back(cur);
}
break;
case 32:
for (const auto& cur : kEGLConfigAttribsRGBA32) {
attribs.push_back(cur);
}
break;
default:
NS_ERROR("Unknown pixel depth");
return false;
}
if (aUseGles) {
attribs.push_back(LOCAL_EGL_RENDERABLE_TYPE);
attribs.push_back(LOCAL_EGL_OPENGL_ES2_BIT);
}
for (const auto& cur : kTerminationAttribs) {
attribs.push_back(cur);
}
if (!egl.fChooseConfig(attribs.data(), configs, ncfg, &ncfg) || ncfg < 1) {
return false;
}
Maybe<EGLConfig> fallbackConfig;
for (int j = 0; j < ncfg; ++j) {
EGLConfig config = configs[j];
EGLint r, g, b, a;
if (egl.fGetConfigAttrib(config, LOCAL_EGL_RED_SIZE, &r) &&
egl.fGetConfigAttrib(config, LOCAL_EGL_GREEN_SIZE, &g) &&
egl.fGetConfigAttrib(config, LOCAL_EGL_BLUE_SIZE, &b) &&
egl.fGetConfigAttrib(config, LOCAL_EGL_ALPHA_SIZE, &a) &&
((depth == 16 && r == 5 && g == 6 && b == 5) ||
(depth == 24 && r == 8 && g == 8 && b == 8) ||
(depth == 32 && r == 8 && g == 8 && b == 8 && a == 8))) {
EGLint z;
if (aEnableDepthBuffer) {
if (!egl.fGetConfigAttrib(config, LOCAL_EGL_DEPTH_SIZE, &z) ||
z != 24) {
continue;
}
}
if (kIsX11 && aVisual) {
int vis;
if (!egl.fGetConfigAttrib(config, LOCAL_EGL_NATIVE_VISUAL_ID, &vis) ||
aVisual != vis) {
if (!fallbackConfig) {
fallbackConfig = Some(config);
}
continue;
}
}
*aConfig = config;
return true;
}
}
// We don't have a frame buffer X11 visual which matches the EGL visual
// from GLContextEGL::FindVisual(). Let's try to use the fallback one and hope
// we're not on NVIDIA (Bug 1478454) as it causes X11 BadMatch error there.
if (kIsX11 && fallbackConfig) {
*aConfig = fallbackConfig.value();
return true;
}
return false;
}
// Return true if a suitable EGLConfig was found and pass it out
// through aConfig. Return false otherwise.
//
// NB: It's entirely legal for the returned EGLConfig to be valid yet
// have the value null.
// aVisual is used in Linux only.
static bool CreateConfigScreen(EglDisplay& egl, EGLConfig* const aConfig,
const bool aEnableDepthBuffer,
const bool aUseGles, int aVisual) {
int32_t depth = gfxVars::ScreenDepth();
if (CreateConfig(egl, aConfig, depth, aEnableDepthBuffer, aUseGles,
aVisual)) {
return true;
}
#ifdef MOZ_WIDGET_ANDROID
// Bug 736005
// Android doesn't always support 16 bit so also try 24 bit
if (depth == 16) {
return CreateConfig(egl, aConfig, 24, aEnableDepthBuffer, aUseGles);
}
// Bug 970096
// Some devices that have 24 bit screens only support 16 bit OpenGL?
if (depth == 24) {
return CreateConfig(egl, aConfig, 16, aEnableDepthBuffer, aUseGles);
}
#endif
return false;
}
already_AddRefed<GLContext> GLContextProviderEGL::CreateForCompositorWidget(
CompositorWidget* aCompositorWidget, bool aWebRender,
bool /*aForceAccelerated*/) {
EGLNativeWindowType window = nullptr;
int32_t depth = 0;
if (aCompositorWidget) {
window = GET_NATIVE_WINDOW_FROM_COMPOSITOR_WIDGET(aCompositorWidget);
#if defined(MOZ_WIDGET_GTK)
depth = aCompositorWidget->AsX11()->GetDepth();
#endif
}
return GLContextEGLFactory::Create(window, aWebRender, depth);
}
EGLSurface GLContextEGL::CreateCompatibleSurface(void* aWindow) const {
MOZ_ASSERT(aWindow);
if (mConfig == EGL_NO_CONFIG) {
MOZ_CRASH("GFX: Failed with invalid EGLConfig 2!");
}
const auto fnCreate = [&](const bool useGles) -> EGLSurface {
// NOTE: aWindow is an ANativeWindow
auto config = mConfig;
if (!config && !CreateConfigScreen(*mEgl, &config,
/* aEnableDepthBuffer */ false,
/* useGles */ useGles)) {
return nullptr;
}
return mEgl->fCreateWindowSurface(
config, reinterpret_cast<EGLNativeWindowType>(aWindow), 0);
};
auto surface = fnCreate(false);
if (!surface) {
surface = fnCreate(true);
}
if (!surface) {
MOZ_CRASH("GFX: Failed to create EGLSurface 2!");
}
return surface;
}
static void FillContextAttribs(bool es3, bool useGles, nsTArray<EGLint>* out) {
out->AppendElement(LOCAL_EGL_SURFACE_TYPE);
if (IsWaylandDisplay()) {
// Wayland on desktop does not support PBuffer or FBO.
// We create a dummy wl_egl_window instead.
out->AppendElement(LOCAL_EGL_WINDOW_BIT);
} else {
out->AppendElement(LOCAL_EGL_PBUFFER_BIT);
}
if (useGles) {
out->AppendElement(LOCAL_EGL_RENDERABLE_TYPE);
if (es3) {
out->AppendElement(LOCAL_EGL_OPENGL_ES3_BIT_KHR);
} else {
out->AppendElement(LOCAL_EGL_OPENGL_ES2_BIT);
}
}
out->AppendElement(LOCAL_EGL_RED_SIZE);
out->AppendElement(8);
out->AppendElement(LOCAL_EGL_GREEN_SIZE);
out->AppendElement(8);
out->AppendElement(LOCAL_EGL_BLUE_SIZE);
out->AppendElement(8);
out->AppendElement(LOCAL_EGL_ALPHA_SIZE);
out->AppendElement(8);
out->AppendElement(LOCAL_EGL_DEPTH_SIZE);
out->AppendElement(0);
out->AppendElement(LOCAL_EGL_STENCIL_SIZE);
out->AppendElement(0);
// EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS
out->AppendElement(LOCAL_EGL_NONE);
out->AppendElement(0);
out->AppendElement(0);
out->AppendElement(0);
}
/*
/// Useful for debugging, but normally unused.
static GLint GetAttrib(GLLibraryEGL* egl, EGLConfig config, EGLint attrib) {
EGLint bits = 0;
egl->fGetConfigAttrib(config, attrib, &bits);
MOZ_ASSERT(egl->fGetError() == LOCAL_EGL_SUCCESS);
return bits;
}
*/
static EGLConfig ChooseConfig(EglDisplay& egl, const GLContextCreateDesc& desc,
const bool useGles) {
nsTArray<EGLint> configAttribList;
FillContextAttribs(bool(desc.flags & CreateContextFlags::PREFER_ES3), useGles,
&configAttribList);
const EGLint* configAttribs = configAttribList.Elements();
// The sorting dictated by the spec for eglChooseConfig reasonably assures
// that a reasonable 'best' config is on top.
const EGLint kMaxConfigs = 1;
EGLConfig configs[kMaxConfigs];
EGLint foundConfigs = 0;
if (!egl.fChooseConfig(configAttribs, configs, kMaxConfigs, &foundConfigs) ||
foundConfigs == 0) {
return EGL_NO_CONFIG;
}
EGLConfig config = configs[0];
return config;
}
#ifdef MOZ_X11
/* static */
bool GLContextEGL::FindVisual(bool aUseWebRender, bool useAlpha,
int* const out_visualId) {
nsCString discardFailureId;
const auto egl = DefaultEglDisplay(&discardFailureId);
if (!egl) {
gfxCriticalNote
<< "GLContextEGL::FindVisual(): Failed to load EGL library!";
return false;
}
EGLConfig config;
const int bpp = useAlpha ? 32 : 24;
if (!CreateConfig(*egl, &config, bpp, aUseWebRender, /* aUseGles */ false)) {
gfxCriticalNote
<< "GLContextEGL::FindVisual(): Failed to create EGLConfig!";
return false;
}
if (egl->fGetConfigAttrib(config, LOCAL_EGL_NATIVE_VISUAL_ID, out_visualId)) {
return true;
}
return false;
}
#endif
/*static*/
RefPtr<GLContextEGL> GLContextEGL::CreateEGLPBufferOffscreenContextImpl(
const std::shared_ptr<EglDisplay> egl, const GLContextCreateDesc& desc,
const mozilla::gfx::IntSize& size, const bool useGles,
nsACString* const out_failureId) {
const EGLConfig config = ChooseConfig(*egl, desc, useGles);
if (config == EGL_NO_CONFIG) {
*out_failureId = "FEATURE_FAILURE_EGL_NO_CONFIG"_ns;
NS_WARNING("Failed to find a compatible config.");
return nullptr;
}
if (GLContext::ShouldSpew()) {
egl->DumpEGLConfig(config);
}
mozilla::gfx::IntSize pbSize(size);
EGLSurface surface = nullptr;
if (IsWaylandDisplay()) {
surface = GLContextEGL::CreateWaylandBufferSurface(*egl, config, pbSize);
} else {
surface = GLContextEGL::CreatePBufferSurfaceTryingPowerOfTwo(
*egl, config, LOCAL_EGL_NONE, pbSize);
}
if (!surface) {
*out_failureId = "FEATURE_FAILURE_EGL_POT"_ns;
NS_WARNING("Failed to create PBuffer for context!");
return nullptr;
}
auto fullDesc = GLContextDesc{desc};
fullDesc.isOffscreen = true;
RefPtr<GLContextEGL> gl = GLContextEGL::CreateGLContext(
egl, fullDesc, config, surface, useGles, out_failureId);
if (!gl) {
NS_WARNING("Failed to create GLContext from PBuffer");
egl->fDestroySurface(surface);
#if defined(MOZ_WAYLAND)
DeleteWaylandGLSurface(surface);
#endif
return nullptr;
}
return gl;
}
/*static*/
RefPtr<GLContextEGL> GLContextEGL::CreateEGLPBufferOffscreenContext(
const std::shared_ptr<EglDisplay> display, const GLContextCreateDesc& desc,
const mozilla::gfx::IntSize& size, nsACString* const out_failureId) {
RefPtr<GLContextEGL> gl = CreateEGLPBufferOffscreenContextImpl(
display, desc, size, /* useGles */ false, out_failureId);
if (!gl) {
gl = CreateEGLPBufferOffscreenContextImpl(
display, desc, size, /* useGles */ true, out_failureId);
}
return gl;
}
/*static*/
already_AddRefed<GLContext> GLContextProviderEGL::CreateHeadless(
const GLContextCreateDesc& desc, nsACString* const out_failureId) {
const auto display = DefaultEglDisplay(out_failureId);
if (!display) {
return nullptr;
}
mozilla::gfx::IntSize dummySize = mozilla::gfx::IntSize(16, 16);
auto ret = GLContextEGL::CreateEGLPBufferOffscreenContext(
display, desc, dummySize, out_failureId);
return ret.forget();
}
// Don't want a global context on Android as 1) share groups across 2 threads
// fail on many Tegra drivers (bug 759225) and 2) some mobile devices have a
// very strict limit on global number of GL contexts (bug 754257) and 3) each
// EGL context eats 750k on B2G (bug 813783)
/*static*/
GLContext* GLContextProviderEGL::GetGlobalContext() { return nullptr; }
// -
static StaticMutex sMutex;
static StaticRefPtr<GLLibraryEGL> gDefaultEglLibrary;
RefPtr<GLLibraryEGL> DefaultEglLibrary(nsACString* const out_failureId) {
StaticMutexAutoLock lock(sMutex);
if (!gDefaultEglLibrary) {
gDefaultEglLibrary = GLLibraryEGL::Create(out_failureId);
if (!gDefaultEglLibrary) {
NS_WARNING("GLLibraryEGL::Create failed");
}
}
return gDefaultEglLibrary.get();
}
// -
/*static*/
void GLContextProviderEGL::Shutdown() {
StaticMutexAutoLock lock(sMutex);
if (!gDefaultEglLibrary) {
return;
}
gDefaultEglLibrary = nullptr;
}
} /* namespace gl */
} /* namespace mozilla */
#undef EGL_ATTRIBS_LIST_SAFE_TERMINATION_WORKING_AROUND_BUGS