blob: 7a650893d1629b32febc4dcbe16ad43dfd2324c5 [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 "EmulationGl.h"
#include <algorithm>
#include <cstring>
#include <optional>
#include <vector>
#include "DisplaySurfaceGl.h"
#include "GLESVersionDetector.h"
#include "OpenGLESDispatch/DispatchTables.h"
#include "OpenGLESDispatch/EGLDispatch.h"
#include "OpenGLESDispatch/GLESv2Dispatch.h"
#include "OpenGLESDispatch/OpenGLDispatchLoader.h"
#include "RenderThreadInfoGl.h"
#include "aemu/base/misc/StringUtils.h"
#include "host-common/GfxstreamFatalError.h"
#include "host-common/feature_control.h"
#include "host-common/logging.h"
#include "host-common/opengl/misc.h"
namespace gfxstream {
namespace gl {
namespace {
static void EGLAPIENTRY EglDebugCallback(EGLenum error,
const char *command,
EGLint messageType,
EGLLabelKHR threadLabel,
EGLLabelKHR objectLabel,
const char *message) {
GL_LOG("command:%s message:%s", command, message);
}
static void GL_APIENTRY GlDebugCallback(GLenum source,
GLenum type,
GLuint id,
GLenum severity,
GLsizei length,
const GLchar *message,
const void *userParam) {
GL_LOG("message:%s", message);
}
static const GLint kGles2ContextAttribsESOrGLCompat[] = {
EGL_CONTEXT_CLIENT_VERSION, 2, //
EGL_NONE, //
};
static const GLint kGles2ContextAttribsCoreGL[] = {
EGL_CONTEXT_CLIENT_VERSION, 2, //
EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR, //
EGL_NONE, //
};
static const GLint kGles3ContextAttribsESOrGLCompat[] = {
EGL_CONTEXT_CLIENT_VERSION, 3, //
EGL_NONE, //
};
static const GLint kGles3ContextAttribsCoreGL[] = {
EGL_CONTEXT_CLIENT_VERSION, 3, //
EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR, //
EGL_NONE, //
};
static bool validateGles2Context(EGLDisplay display) {
const GLint configAttribs[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE,
};
EGLint numConfigs = 0;
EGLConfig config;
if (!s_egl.eglChooseConfig(display, configAttribs, &config, 1, &numConfigs)) {
ERR("Failed to find GLES 2.x config.");
return false;
}
if (numConfigs != 1) {
ERR("Failed to find exactly 1 GLES 2.x config: found %d.", numConfigs);
return false;
}
const EGLint surfaceAttribs[] = {
EGL_WIDTH, 1,
EGL_HEIGHT, 1,
EGL_NONE,
};
EGLSurface surface = s_egl.eglCreatePbufferSurface(display, config, surfaceAttribs);
if (surface == EGL_NO_SURFACE) {
ERR("Failed to create GLES 2.x pbuffer surface.");
return false;
}
const GLint* contextAttribs = EmulationGl::getGlesMaxContextAttribs();
EGLContext context = s_egl.eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (context == EGL_NO_CONTEXT) {
ERR("Failed to create GLES 2.x context.");
s_egl.eglDestroySurface(display, surface);
return false;
}
if (!s_egl.eglMakeCurrent(display, surface, surface, context)) {
ERR("Failed to make GLES 2.x context current.");
s_egl.eglDestroySurface(display, surface);
s_egl.eglDestroyContext(display, context);
return false;
}
const char* extensions = (const char*)s_gles2.glGetString(GL_EXTENSIONS);
if (extensions == nullptr) {
ERR("Failed to query GLES 2.x context extensions.");
s_egl.eglDestroySurface(display, surface);
s_egl.eglDestroyContext(display, context);
return false;
}
// It is rare but some drivers actually fail this...
if (!s_egl.eglMakeCurrent(display, EGL_NO_CONTEXT, EGL_NO_SURFACE, EGL_NO_SURFACE)) {
ERR("Failed to unbind GLES 2.x context.");
s_egl.eglDestroySurface(display, surface);
s_egl.eglDestroyContext(display, context);
return false;
}
s_egl.eglDestroyContext(display, context);
s_egl.eglDestroySurface(display, surface);
return true;
}
static std::optional<EGLConfig> getEmulationEglConfig(EGLDisplay display, bool allowWindowSurface) {
GLint surfaceType = EGL_PBUFFER_BIT;
if (allowWindowSurface) {
surfaceType |= EGL_WINDOW_BIT;
}
// On Linux, we need RGB888 exactly, or eglMakeCurrent will fail,
// as glXMakeContextCurrent needs to match the format of the
// native pixmap.
constexpr const EGLint kWantedRedSize = 8;
constexpr const EGLint kWantedGreenSize = 8;
constexpr const EGLint kWantedBlueSize = 8;
const GLint configAttribs[] = {
EGL_RED_SIZE, kWantedRedSize, //
EGL_GREEN_SIZE, kWantedGreenSize, //
EGL_BLUE_SIZE, kWantedBlueSize, //
EGL_SURFACE_TYPE, surfaceType, //
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //
EGL_NONE, //
};
EGLint numConfigs = 0;
s_egl.eglGetConfigs(display, nullptr, 0, &numConfigs);
std::vector<EGLConfig> configs(numConfigs);
EGLint numMatchedConfigs = 0;
s_egl.eglChooseConfig(display, configAttribs, configs.data(), numConfigs, &numMatchedConfigs);
configs.resize(numMatchedConfigs);
for (EGLConfig config : configs) {
EGLint foundRedSize = 0;
s_egl.eglGetConfigAttrib(display, config, EGL_RED_SIZE, &foundRedSize);
if (foundRedSize != kWantedRedSize) {
continue;
}
EGLint foundGreenSize = 0;
s_egl.eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &foundGreenSize);
if (foundGreenSize != kWantedGreenSize) {
continue;
}
EGLint foundBlueSize = 0;
s_egl.eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &foundBlueSize);
if (foundBlueSize != kWantedBlueSize) {
continue;
}
return config;
}
return std::nullopt;
}
} // namespace
std::unique_ptr<EmulationGl> EmulationGl::create(uint32_t width, uint32_t height,
bool allowWindowSurface, bool egl2egl) {
// Loads the glestranslator function pointers.
if (!LazyLoadedEGLDispatch::get()) {
ERR("Failed to load EGL dispatch.");
return nullptr;
}
if (!LazyLoadedGLESv1Dispatch::get()) {
ERR("Failed to load GLESv1 dispatch.");
return nullptr;
}
if (!LazyLoadedGLESv2Dispatch::get()) {
ERR("Failed to load GLESv2 dispatch.");
return nullptr;
}
if (s_egl.eglUseOsEglApi) {
s_egl.eglUseOsEglApi(egl2egl, EGL_FALSE);
}
std::unique_ptr<EmulationGl> emulationGl(new EmulationGl());
emulationGl->mWidth = width;
emulationGl->mHeight = height;
emulationGl->mEglDisplay = s_egl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (emulationGl->mEglDisplay == EGL_NO_DISPLAY) {
ERR("Failed to get EGL display.");
return nullptr;
}
GL_LOG("call eglInitialize");
if (!s_egl.eglInitialize(emulationGl->mEglDisplay,
&emulationGl->mEglVersionMajor,
&emulationGl->mEglVersionMinor)) {
ERR("Failed to eglInitialize.");
return nullptr;
}
s_egl.eglBindAPI(EGL_OPENGL_ES_API);
#ifdef ENABLE_GL_LOG
if (s_egl.eglDebugMessageControlKHR) {
const EGLAttrib controls[] = {
EGL_DEBUG_MSG_CRITICAL_KHR,
EGL_TRUE,
EGL_DEBUG_MSG_ERROR_KHR,
EGL_TRUE,
EGL_DEBUG_MSG_WARN_KHR,
EGL_TRUE,
EGL_DEBUG_MSG_INFO_KHR,
EGL_FALSE,
EGL_NONE,
EGL_NONE,
};
if (s_egl.eglDebugMessageControlKHR(&EglDebugCallback, controls) == EGL_SUCCESS) {
GL_LOG("Successfully set eglDebugMessageControlKHR");
} else {
GL_LOG("Failed to eglDebugMessageControlKHR");
}
} else {
GL_LOG("eglDebugMessageControlKHR not available");
}
#endif
emulationGl->mEglVendor = s_egl.eglQueryString(emulationGl->mEglDisplay, EGL_VENDOR);
const std::string eglExtensions = s_egl.eglQueryString(emulationGl->mEglDisplay, EGL_EXTENSIONS);
android::base::split<std::string>(eglExtensions, " ",
[&](const std::string& found) {
emulationGl->mEglExtensions.insert(found);
});
if (!emulationGl->hasEglExtension("EGL_KHR_gl_texture_2D_image")) {
ERR("Failed to find required EGL_KHR_gl_texture_2D_image extension.");
return nullptr;
}
emulationGl->mGlesDispatchMaxVersion
= calcMaxVersionFromDispatch(emulationGl->mEglDisplay);
if (s_egl.eglSetMaxGLESVersion) {
// eglSetMaxGLESVersion must be called before any context binding
// because it changes how we initialize the dispatcher table.
s_egl.eglSetMaxGLESVersion(emulationGl->mGlesDispatchMaxVersion);
}
int glesVersionMajor;
int glesVersionMinor;
emugl::getGlesVersion(&glesVersionMajor, &glesVersionMinor);
emulationGl->mGlesVersionMajor = glesVersionMajor;
emulationGl->mGlesVersionMinor = glesVersionMinor;
if (!validateGles2Context(emulationGl->mEglDisplay)) {
ERR("Failed to validate creating GLES 2.x context.");
return nullptr;
}
// TODO (b/207426737): Remove the Imagination-specific workaround.
const bool disableFastBlit =
emulationGl->mEglVendor.find("Imagination Technologies") != std::string::npos;
emulationGl->mFastBlitSupported =
(emulationGl->mGlesDispatchMaxVersion > GLES_DISPATCH_MAX_VERSION_2) &&
!disableFastBlit &&
(emugl::getRenderer() == SELECTED_RENDERER_HOST ||
emugl::getRenderer() == SELECTED_RENDERER_SWIFTSHADER_INDIRECT ||
emugl::getRenderer() == SELECTED_RENDERER_ANGLE_INDIRECT);
auto eglConfigOpt = getEmulationEglConfig(emulationGl->mEglDisplay, allowWindowSurface);
if (!eglConfigOpt) {
ERR("Failed to find config for emulation GL.");
return nullptr;
}
emulationGl->mEglConfig = *eglConfigOpt;
const GLint* maxContextAttribs = getGlesMaxContextAttribs();
emulationGl->mEglContext = s_egl.eglCreateContext(emulationGl->mEglDisplay,
emulationGl->mEglConfig,
EGL_NO_CONTEXT,
maxContextAttribs);
if (emulationGl->mEglContext == EGL_NO_CONTEXT) {
ERR("Failed to create context, error 0x%x.", s_egl.eglGetError());
return nullptr;
}
// Create another context which shares with the default context to be
// used when we bind the pbuffer. This prevents switching the drawable
// binding back and forth on the framebuffer context.
// The main purpose of it is to solve a "blanking" behaviour we see on
// on Mac platform when switching binded drawable for a context however
// it is more efficient on other platforms as well.
auto pbufferSurfaceGl = DisplaySurfaceGl::createPbufferSurface(emulationGl->mEglDisplay,
emulationGl->mEglConfig,
emulationGl->mEglContext,
maxContextAttribs,
/*width=*/1,
/*height=*/1);
if (!pbufferSurfaceGl) {
ERR("Failed to create pbuffer display surface.");
return nullptr;
}
auto* pbufferSurfaceGlPtr = pbufferSurfaceGl.get();
emulationGl->mPbufferSurface = std::make_unique<gfxstream::DisplaySurface>(
/*width=*/1,
/*height=*/1,
std::move(pbufferSurfaceGl));
// b/283491732: we could skip the creation of subwindow if we know we will create a real
// window.
auto fakeWindowSurfaceGl = DisplaySurfaceGl::createPbufferSurface(emulationGl->mEglDisplay,
emulationGl->mEglConfig,
emulationGl->mEglContext,
maxContextAttribs,
width,
height);
if (!fakeWindowSurfaceGl) {
ERR("Failed to create fake window display surface.");
return nullptr;
}
emulationGl->mFakeWindowSurface = std::make_unique<gfxstream::DisplaySurface>(
width,
height,
std::move(fakeWindowSurfaceGl));
emulationGl->mEmulatedEglConfigs =
std::make_unique<EmulatedEglConfigList>(emulationGl->mEglDisplay,
emulationGl->mGlesDispatchMaxVersion);
if (emulationGl->mEmulatedEglConfigs->empty()) {
ERR("Failed to initialize emulated configs.");
return nullptr;
}
const bool hasEsOrEs2Context =
std::any_of(emulationGl->mEmulatedEglConfigs->begin(),
emulationGl->mEmulatedEglConfigs->end(),
[](const EmulatedEglConfig& config) {
const GLint renderableType = config.getRenderableType();
return renderableType & (EGL_OPENGL_ES_BIT | EGL_OPENGL_ES2_BIT);
});
if (!hasEsOrEs2Context) {
ERR("Failed to find any usable guest EGL configs.");
return nullptr;
}
RecursiveScopedContextBind contextBind(pbufferSurfaceGlPtr->getContextHelper());
if (!contextBind.isOk()) {
ERR("Failed to make pbuffer context and surface current");
return nullptr;
}
#ifdef ENABLE_GL_LOG
bool debugSetup = false;
if (s_gles2.glDebugMessageCallback) {
s_gles2.glEnable(GL_DEBUG_OUTPUT);
s_gles2.glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
s_gles2.glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
GL_DEBUG_SEVERITY_HIGH, 0, nullptr, GL_TRUE);
s_gles2.glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr, GL_TRUE);
s_gles2.glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
GL_DEBUG_SEVERITY_LOW, 0, nullptr, GL_TRUE);
s_gles2.glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr,
GL_TRUE);
s_gles2.glDebugMessageCallback(&GlDebugCallback, nullptr);
debugSetup = s_gles2.glGetError() == GL_NO_ERROR;
if (!debugSetup) {
ERR("Failed to set up glDebugMessageCallback");
} else {
GL_LOG("Successfully set up glDebugMessageCallback");
}
}
if (s_gles2.glDebugMessageCallbackKHR && !debugSetup) {
s_gles2.glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE,
GL_DEBUG_SEVERITY_HIGH_KHR, 0, nullptr,
GL_TRUE);
s_gles2.glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE,
GL_DEBUG_SEVERITY_MEDIUM_KHR, 0, nullptr,
GL_TRUE);
s_gles2.glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE,
GL_DEBUG_SEVERITY_LOW_KHR, 0, nullptr,
GL_TRUE);
s_gles2.glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE,
GL_DEBUG_SEVERITY_NOTIFICATION_KHR, 0, nullptr,
GL_TRUE);
s_gles2.glDebugMessageCallbackKHR(&GlDebugCallback, nullptr);
debugSetup = s_gles2.glGetError() == GL_NO_ERROR;
if (!debugSetup) {
ERR("Failed to set up glDebugMessageCallbackKHR");
} else {
GL_LOG("Successfully set up glDebugMessageCallbackKHR");
}
}
if (!debugSetup) {
GL_LOG("glDebugMessageCallback and glDebugMessageCallbackKHR not available");
}
#endif
emulationGl->mGlesVendor = (const char*)s_gles2.glGetString(GL_VENDOR);
emulationGl->mGlesRenderer = (const char*)s_gles2.glGetString(GL_RENDERER);
emulationGl->mGlesVersion = (const char*)s_gles2.glGetString(GL_VERSION);
emulationGl->mGlesExtensions = (const char*)s_gles2.glGetString(GL_EXTENSIONS);
s_gles2.glGetError();
GLint numDeviceUuids = 0;
s_gles2.glGetIntegerv(GL_NUM_DEVICE_UUIDS_EXT, &numDeviceUuids);
if (numDeviceUuids == 1) {
GlesUuid uuid{};
s_gles2.glGetUnsignedBytei_vEXT(GL_DEVICE_UUID_EXT, 0, uuid.data());
emulationGl->mGlesDeviceUuid = uuid;
}
emulationGl->mGlesVulkanInteropSupported = false;
if (s_egl.eglQueryVulkanInteropSupportANDROID) {
emulationGl->mGlesVulkanInteropSupported = s_egl.eglQueryVulkanInteropSupportANDROID();
}
emulationGl->mTextureDraw = std::make_unique<TextureDraw>();
if (!emulationGl->mTextureDraw) {
ERR("Failed to initialize TextureDraw.");
return nullptr;
}
emulationGl->mCompositorGl = std::make_unique<CompositorGl>(emulationGl->mTextureDraw.get());
emulationGl->mDisplayGl = std::make_unique<DisplayGl>(emulationGl->mTextureDraw.get());
{
auto surface1 = DisplaySurfaceGl::createPbufferSurface(emulationGl->mEglDisplay,
emulationGl->mEglConfig,
emulationGl->mEglContext,
getGlesMaxContextAttribs(),
/*width=*/1,
/*height=*/1);
if (!surface1) {
ERR("Failed to create pbuffer surface for ReadbackWorkerGl.");
return nullptr;
}
auto surface2 = DisplaySurfaceGl::createPbufferSurface(emulationGl->mEglDisplay,
emulationGl->mEglConfig,
emulationGl->mEglContext,
getGlesMaxContextAttribs(),
/*width=*/1,
/*height=*/1);
if (!surface2) {
ERR("Failed to create pbuffer surface for ReadbackWorkerGl.");
return nullptr;
}
emulationGl->mReadbackWorkerGl = std::make_unique<ReadbackWorkerGl>(std::move(surface1),
std::move(surface2));
}
return emulationGl;
}
EmulationGl::~EmulationGl() {
if (mPbufferSurface) {
// TODO(b/267349580): remove after Mac issue fixed.
mTextureDraw.release();
// const auto* displaySurfaceGl =
// reinterpret_cast<const DisplaySurfaceGl*>(mPbufferSurface->getImpl());
// RecursiveScopedContextBind contextBind(displaySurfaceGl->getContextHelper());
// if (contextBind.isOk()) {
// mTextureDraw.reset();
// } else {
// ERR("Failed to bind context for destroying TextureDraw.");
// }
}
if (mEglDisplay != EGL_NO_DISPLAY) {
s_egl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
if (mEglContext != EGL_NO_CONTEXT) {
s_egl.eglDestroyContext(mEglDisplay, mEglContext);
mEglContext = EGL_NO_CONTEXT;
}
mEglDisplay = EGL_NO_DISPLAY;
}
}
gfxstream::DisplaySurface* EmulationGl::getFakeWindowSurface() { return mFakeWindowSurface.get(); }
/*static*/ const GLint* EmulationGl::getGlesMaxContextAttribs() {
int glesMaj, glesMin;
emugl::getGlesVersion(&glesMaj, &glesMin);
if (shouldEnableCoreProfile()) {
if (glesMaj == 2) {
return kGles2ContextAttribsCoreGL;
} else {
return kGles3ContextAttribsCoreGL;
}
}
if (glesMaj == 2) {
return kGles2ContextAttribsESOrGLCompat;
} else {
return kGles3ContextAttribsESOrGLCompat;
}
}
const EGLDispatch* EmulationGl::getEglDispatch() {
return &s_egl;
}
const GLESv2Dispatch* EmulationGl::getGles2Dispatch() {
return &s_gles2;
}
GLESDispatchMaxVersion EmulationGl::getGlesMaxDispatchVersion() const {
return mGlesDispatchMaxVersion;
}
bool EmulationGl::hasEglExtension(const std::string& ext) const {
return mEglExtensions.find(ext) != mEglExtensions.end();
}
void EmulationGl::getEglVersion(EGLint* major, EGLint* minor) const {
if (major) {
*major = mEglVersionMajor;
}
if (minor) {
*minor = mEglVersionMinor;
}
}
void EmulationGl::getGlesVersion(GLint* major, GLint* minor) const {
if (major) {
*major = mGlesVersionMajor;
}
if (minor) {
*minor = mGlesVersionMinor;
}
}
bool EmulationGl::isMesa() const { return mGlesVersion.find("Mesa") != std::string::npos; }
bool EmulationGl::isFastBlitSupported() const {
return mFastBlitSupported;
}
void EmulationGl::disableFastBlitForTesting() {
mFastBlitSupported = false;
}
bool EmulationGl::isAsyncReadbackSupported() const {
return mGlesVersionMajor > 2;
}
std::unique_ptr<DisplaySurface> EmulationGl::createWindowSurface(
uint32_t width,
uint32_t height,
EGLNativeWindowType window) {
auto surfaceGl = DisplaySurfaceGl::createWindowSurface(mEglDisplay,
mEglConfig,
mEglContext,
getGlesMaxContextAttribs(),
window);
if (!surfaceGl) {
ERR("Failed to create DisplaySurfaceGl.");
return nullptr;
}
return std::make_unique<DisplaySurface>(width,
height,
std::move(surfaceGl));
}
ContextHelper* EmulationGl::getColorBufferContextHelper() {
if (!mPbufferSurface) {
return nullptr;
}
const auto* surfaceGl = static_cast<const DisplaySurfaceGl*>(mPbufferSurface->getImpl());
return surfaceGl->getContextHelper();
}
std::unique_ptr<BufferGl> EmulationGl::createBuffer(uint64_t size, HandleType handle) {
return BufferGl::create(size, handle, getColorBufferContextHelper());
}
std::unique_ptr<BufferGl> EmulationGl::loadBuffer(android::base::Stream* stream) {
return BufferGl::onLoad(stream, getColorBufferContextHelper());
}
std::unique_ptr<ColorBufferGl> EmulationGl::createColorBuffer(uint32_t width, uint32_t height,
GLenum internalFormat,
FrameworkFormat frameworkFormat,
HandleType handle) {
return ColorBufferGl::create(mEglDisplay, width, height, internalFormat, frameworkFormat,
handle, getColorBufferContextHelper(), mTextureDraw.get(),
isFastBlitSupported());
}
std::unique_ptr<ColorBufferGl> EmulationGl::loadColorBuffer(android::base::Stream* stream) {
return ColorBufferGl::onLoad(stream, mEglDisplay, getColorBufferContextHelper(),
mTextureDraw.get(), isFastBlitSupported());
}
std::unique_ptr<EmulatedEglContext> EmulationGl::createEmulatedEglContext(
uint32_t emulatedEglConfigIndex,
const EmulatedEglContext* sharedContext,
GLESApi api,
HandleType handle) {
if (!mEmulatedEglConfigs) {
ERR("EmulatedEglConfigs unavailable.");
return nullptr;
}
const EmulatedEglConfig* emulatedEglConfig = mEmulatedEglConfigs->get(emulatedEglConfigIndex);
if (!emulatedEglConfig) {
ERR("Failed to find emulated EGL config %d", emulatedEglConfigIndex);
return nullptr;
}
EGLConfig config = emulatedEglConfig->getHostEglConfig();
EGLContext share = sharedContext ? sharedContext->getEGLContext() : EGL_NO_CONTEXT;
return EmulatedEglContext::create(mEglDisplay, config, share, handle, api);
}
std::unique_ptr<EmulatedEglContext> EmulationGl::loadEmulatedEglContext(
android::base::Stream* stream) {
return EmulatedEglContext::onLoad(stream, mEglDisplay);
}
std::unique_ptr<EmulatedEglFenceSync> EmulationGl::createEmulatedEglFenceSync(
EGLenum type,
int destroyWhenSignaled) {
const bool hasNativeFence = type == EGL_SYNC_NATIVE_FENCE_ANDROID;
return EmulatedEglFenceSync::create(mEglDisplay,
hasNativeFence,
destroyWhenSignaled);
}
std::unique_ptr<EmulatedEglImage> EmulationGl::createEmulatedEglImage(
EmulatedEglContext* context,
EGLenum target,
EGLClientBuffer buffer) {
EGLContext eglContext = context ? context->getEGLContext() : EGL_NO_CONTEXT;
return EmulatedEglImage::create(mEglDisplay, eglContext, target, buffer);
}
std::unique_ptr<EmulatedEglWindowSurface> EmulationGl::createEmulatedEglWindowSurface(
uint32_t emulatedConfigIndex,
uint32_t width,
uint32_t height,
HandleType handle) {
if (!mEmulatedEglConfigs) {
ERR("EmulatedEglConfigs unavailable.");
return nullptr;
}
const EmulatedEglConfig* emulatedEglConfig = mEmulatedEglConfigs->get(emulatedConfigIndex);
if (!emulatedEglConfig) {
ERR("Failed to find emulated EGL config %d", emulatedConfigIndex);
return nullptr;
}
EGLConfig config = emulatedEglConfig->getHostEglConfig();
return EmulatedEglWindowSurface::create(mEglDisplay, config, width, height, handle);
}
std::unique_ptr<EmulatedEglWindowSurface> EmulationGl::loadEmulatedEglWindowSurface(
android::base::Stream* stream,
const ColorBufferMap& colorBuffers,
const EmulatedEglContextMap& contexts) {
return EmulatedEglWindowSurface::onLoad(stream, mEglDisplay, colorBuffers, contexts);
}
} // namespace gl
} // namespace gfxstream