blob: 88fff8ddd6390498ba3a0b74d72a70f7e5cce485 [file] [log] [blame]
// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "DisplaySurfaceGl.h"
#include <vector>
#include <stdio.h>
#include "OpenGLESDispatch/DispatchTables.h"
#include "OpenGLESDispatch/EGLDispatch.h"
#include "host-common/GfxstreamFatalError.h"
#include "host-common/logging.h"
namespace gfxstream {
namespace gl {
namespace {
using emugl::ABORT_REASON_OTHER;
using emugl::FatalError;
struct PreviousContextInfo {
EGLContext context = EGL_NO_CONTEXT;
EGLSurface readSurface = EGL_NO_SURFACE;
EGLSurface drawSurface = EGL_NO_SURFACE;
};
struct ThreadState {
std::vector<PreviousContextInfo> previousContexts;
};
static thread_local ThreadState sThreadState;
class DisplaySurfaceGlContextHelper : public ContextHelper {
public:
DisplaySurfaceGlContextHelper(EGLDisplay display,
EGLSurface surface,
EGLContext context)
: mDisplay(display),
mSurface(surface),
mContext(context) {
if (mDisplay == EGL_NO_DISPLAY) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "DisplaySurfaceGlContextHelper created with no display?";
}
if (mSurface == EGL_NO_SURFACE) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "DisplaySurfaceGlContextHelper created with no surface?";
}
if (mContext == EGL_NO_CONTEXT) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "DisplaySurfaceGlContextHelper created with no context?";
}
}
bool setupContext() override {
auto& previousContexts = sThreadState.previousContexts;
EGLContext currentContext = s_egl.eglGetCurrentContext();
EGLSurface currentDrawSurface = s_egl.eglGetCurrentSurface(EGL_DRAW);
EGLSurface currentReadSurface = s_egl.eglGetCurrentSurface(EGL_READ);
bool needsUpdate = (currentContext != mContext ||
currentDrawSurface != mSurface ||
currentReadSurface != mSurface);
if (needsUpdate) {
if (!previousContexts.empty()) {
ERR("DisplaySurfaceGlContextHelper context was preempted by others, "
"current=%p, needed=%p, thread=%p", currentContext, mContext, &sThreadState);
// Fall through to attempt to recover from error.
}
if (!s_egl.eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
// b/284523053
// Legacy swiftshader logspam on exit with this line.
GL_LOG("Failed to make display surface context current: %d", s_egl.eglGetError());
// Fall through to allow adding previous context to stack.
}
}
previousContexts.push_back(
{.context = currentContext,
.readSurface = currentReadSurface,
.drawSurface = currentDrawSurface});
return true;
}
void teardownContext() override {
auto& previousContexts = sThreadState.previousContexts;
EGLContext currentContext = s_egl.eglGetCurrentContext();
EGLSurface currentDrawSurface = s_egl.eglGetCurrentSurface(EGL_DRAW);
EGLSurface currentReadSurface = s_egl.eglGetCurrentSurface(EGL_READ);
PreviousContextInfo newContext;
if (!previousContexts.empty()) {
newContext = previousContexts.back();
previousContexts.pop_back();
}
bool needsUpdate = (currentContext != newContext.context ||
currentDrawSurface != newContext.drawSurface ||
currentReadSurface != newContext.readSurface);
if (!needsUpdate) {
return;
}
if (!s_egl.eglMakeCurrent(mDisplay,
newContext.drawSurface,
newContext.readSurface,
newContext.context)) {
ERR("Failed to restore previous context: %d", s_egl.eglGetError());
}
}
bool isBound() const override { return !sThreadState.previousContexts.empty(); }
private:
EGLDisplay mDisplay = EGL_NO_DISPLAY;
EGLSurface mSurface = EGL_NO_SURFACE;
EGLContext mContext = EGL_NO_CONTEXT;
};
} // namespace
/*static*/
std::unique_ptr<DisplaySurfaceGl> DisplaySurfaceGl::createPbufferSurface(
EGLDisplay display,
EGLConfig config,
EGLContext shareContext,
const EGLint* contextAttribs,
EGLint width,
EGLint height) {
EGLContext context = s_egl.eglCreateContext(display, config, shareContext, contextAttribs);
if (context == EGL_NO_CONTEXT) {
ERR("Failed to create context for DisplaySurfaceGl.");
return nullptr;
}
const EGLint surfaceAttribs[] = {
EGL_WIDTH, width, //
EGL_HEIGHT, height, //
EGL_NONE, //
};
EGLSurface surface = s_egl.eglCreatePbufferSurface(display, config, surfaceAttribs);
if (surface == EGL_NO_SURFACE) {
ERR("Failed to create pbuffer surface for DisplaySurfaceGl.");
return nullptr;
}
return std::unique_ptr<DisplaySurfaceGl>(new DisplaySurfaceGl(display, surface, context));
}
/*static*/
std::unique_ptr<DisplaySurfaceGl> DisplaySurfaceGl::createWindowSurface(
EGLDisplay display,
EGLConfig config,
EGLContext shareContext,
const GLint* contextAttribs,
FBNativeWindowType window) {
EGLContext context = s_egl.eglCreateContext(display, config, shareContext, contextAttribs);
if (context == EGL_NO_CONTEXT) {
ERR("Failed to create context for DisplaySurfaceGl.");
return nullptr;
}
EGLSurface surface = s_egl.eglCreateWindowSurface(display, config, window, nullptr);
if (surface == EGL_NO_SURFACE) {
ERR("Failed to create window surface for DisplaySurfaceGl.");
return nullptr;
}
return std::unique_ptr<DisplaySurfaceGl>(new DisplaySurfaceGl(display, surface, context));
}
bool DisplaySurfaceGl::bindContext() const {
if (!s_egl.eglMakeCurrent(mDisplay, mSurface, mSurface, mContext)) {
ERR("Failed to make display surface context current: %d", s_egl.eglGetError());
return false;
}
return true;
}
DisplaySurfaceGl::DisplaySurfaceGl(EGLDisplay display,
EGLSurface surface,
EGLContext context)
: mDisplay(display),
mSurface(surface),
mContext(context),
mContextHelper(new DisplaySurfaceGlContextHelper(display, surface, context)) {}
DisplaySurfaceGl::~DisplaySurfaceGl() {
if (mDisplay != EGL_NO_DISPLAY) {
if (mSurface) {
s_egl.eglDestroySurface(mDisplay, mSurface);
}
if (mContext) {
s_egl.eglDestroyContext(mDisplay, mContext);
}
}
}
} // namespace gl
} // namespace gfxstream