blob: 793cdbb6c284439645136b9dba65203df0cd3488 [file] [log] [blame]
/*
* Copyright (C) 2017 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 "EglOsApi.h"
#include "base/System.h"
#include "base/SharedLibrary.h"
#include "host-common/logging.h"
#include "host-common/misc.h"
#include "GLcommon/GLLibrary.h"
#include "ShaderCache.h"
#ifdef ANDROID
#include <android/native_window.h>
#endif
#ifdef __linux__
#include "apigen-codec-common/X11Support.h"
#endif
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <EGL/eglext_angle.h>
#include <GLES2/gl2.h>
#include <cstring>
#include <memory>
#include <vector>
#define DEBUG 0
#if DEBUG
#define D(...) fprintf(stderr, __VA_ARGS__);
#define CHECK_EGL_ERR \
{ \
EGLint err = mDispatcher.eglGetError(); \
if (err != EGL_SUCCESS) \
D("%s: %s %d get egl error %d\n", __FUNCTION__, __FILE__, \
__LINE__, err); \
}
#else
#define D(...) ((void)0);
#define CHECK_EGL_ERR ((void)0);
#endif
#if defined(__WIN32) || defined(_MSC_VER)
static const char* kEGLLibName = "libEGL.dll";
static const char* kGLES2LibName = "libGLESv2.dll";
#elif defined(__linux__)
static const char* kEGLLibName = "libEGL.so";
static const char* kGLES2LibName = "libGLESv2.so";
static const char* kEGLLibNameAlt = "libEGL.so.1";
static const char* kGLES2LibNameAlt = "libGLESv2.so.2";
#else // __APPLE__
#include "MacNative.h"
static const char* kEGLLibName = "libEGL.dylib";
static const char* kGLES2LibName = "libGLESv2.dylib";
#endif // __APPLE__
// List of EGL functions of interest to probe with GetProcAddress()
#define LIST_EGL_FUNCTIONS(X) \
X(void*, eglGetProcAddress, \
(const char* procname)) \
X(const char*, eglQueryString, \
(EGLDisplay dpy, EGLint id)) \
X(EGLDisplay, eglGetPlatformDisplay, \
(EGLenum platform, void *native_display, const EGLAttrib *attrib_list)) \
X(EGLDisplay, eglGetPlatformDisplayEXT, \
(EGLenum platform, void *native_display, const EGLint *attrib_list)) \
X(EGLBoolean, eglBindAPI, \
(EGLenum api)) \
X(EGLBoolean, eglChooseConfig, \
(EGLDisplay display, EGLint const* attrib_list, EGLConfig* configs, \
EGLint config_size, EGLint* num_config)) \
X(EGLContext, eglCreateContext, \
(EGLDisplay display, EGLConfig config, EGLContext share_context, \
EGLint const* attrib_list)) \
X(EGLSurface, eglCreatePbufferSurface, \
(EGLDisplay display, EGLConfig config, EGLint const* attrib_list)) \
X(EGLBoolean, eglDestroyContext, (EGLDisplay display, EGLContext context)) \
X(EGLBoolean, eglDestroySurface, (EGLDisplay display, EGLSurface surface)) \
X(EGLBoolean, eglGetConfigAttrib, \
(EGLDisplay display, EGLConfig config, EGLint attribute, \
EGLint * value)) \
X(EGLDisplay, eglGetDisplay, (NativeDisplayType native_display)) \
X(EGLint, eglGetError, (void)) \
X(EGLBoolean, eglInitialize, \
(EGLDisplay display, EGLint * major, EGLint * minor)) \
X(EGLBoolean, eglMakeCurrent, \
(EGLDisplay display, EGLSurface draw, EGLSurface read, \
EGLContext context)) \
X(EGLBoolean, eglSwapBuffers, (EGLDisplay display, EGLSurface surface)) \
X(EGLSurface, eglCreateWindowSurface, \
(EGLDisplay display, EGLConfig config, \
EGLNativeWindowType native_window, EGLint const* attrib_list)) \
X(EGLBoolean, eglSwapInterval, \
(EGLDisplay display, EGLint interval)) \
X(void, eglSetBlobCacheFuncsANDROID, (EGLDisplay display, \
EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get)) \
X(EGLImage, eglCreateImageKHR, (EGLDisplay dpy, \
EGLContext ctx, EGLenum target, EGLClientBuffer buffer, \
const EGLint *attrib_list)) \
X(EGLBoolean, eglDestroyImageKHR, (EGLDisplay dpy, EGLImage image)) \
X(EGLImage, eglCreateImage, (EGLDisplay dpy, \
EGLContext ctx, EGLenum target, EGLClientBuffer buffer, \
const EGLAttrib *attrib_list)) \
X(EGLBoolean, eglDestroyImage, (EGLDisplay dpy, EGLImage image)) \
X(EGLBoolean, eglReleaseThread, (void)) \
X(EGLint, eglDebugMessageControlKHR, \
(EGLDEBUGPROCKHR callback, const EGLAttrib * attrib_list)) \
namespace {
using namespace EglOS;
class EglOsEglDispatcher {
public:
#define DECLARE_EGL_POINTER(return_type, function_name, signature) \
return_type(EGLAPIENTRY* function_name) signature = nullptr;
LIST_EGL_FUNCTIONS(DECLARE_EGL_POINTER);
EglOsEglDispatcher() {
D("loading %s\n", kEGLLibName);
char error[256];
mLib = android::base::SharedLibrary::open(kEGLLibName, error, sizeof(error));
if (!mLib) {
#ifdef __linux__
ERR("%s: Could not open EGL library %s [%s]. Trying again with [%s]\n", __FUNCTION__,
kEGLLibName, error, kEGLLibNameAlt);
mLib = android::base::SharedLibrary::open(kEGLLibNameAlt, error, sizeof(error));
if (!mLib) {
ERR("%s: Could not open EGL library %s [%s]\n", __FUNCTION__,
kEGLLibNameAlt, error);
}
#else
ERR("%s: Could not open EGL library %s [%s]\n", __FUNCTION__,
kEGLLibName, error);
#endif
}
#define LOAD_EGL_POINTER(return_type, function_name, signature) \
this->function_name = \
reinterpret_cast<return_type(GL_APIENTRY*) signature>( \
mLib->findSymbol(#function_name)); \
if (!this->function_name) { \
this->function_name = \
reinterpret_cast<return_type(GL_APIENTRY*) signature>( \
this->eglGetProcAddress(#function_name)); \
} \
if (!this->function_name) { \
D("%s: Could not find %s in underlying EGL library\n", \
__FUNCTION__, \
#function_name); \
}
LIST_EGL_FUNCTIONS(LOAD_EGL_POINTER);
}
~EglOsEglDispatcher() = default;
private:
android::base::SharedLibrary* mLib = nullptr;
};
class EglOsGlLibrary : public GlLibrary {
public:
EglOsGlLibrary() {
char error[256];
mLib = android::base::SharedLibrary::open(kGLES2LibName, error, sizeof(error));
if (!mLib) {
#ifdef __linux__
ERR("%s: Could not open GL library %s [%s]. Trying again with [%s]\n", __FUNCTION__,
kGLES2LibName, error, kGLES2LibNameAlt);
mLib = android::base::SharedLibrary::open(kGLES2LibNameAlt, error, sizeof(error));
if (!mLib) {
ERR("%s: Could not open GL library %s [%s]\n", __FUNCTION__,
kGLES2LibNameAlt, error);
}
#else
ERR("%s: Could not open GL library %s [%s]\n", __FUNCTION__,
kGLES2LibName, error);
#endif
}
}
GlFunctionPointer findSymbol(const char* name) {
if (!mLib) {
return NULL;
}
return reinterpret_cast<GlFunctionPointer>(mLib->findSymbol(name));
}
~EglOsGlLibrary() = default;
private:
android::base::SharedLibrary* mLib = nullptr;
};
class EglOsEglPixelFormat : public EglOS::PixelFormat {
public:
EglOsEglPixelFormat(EGLConfig configId, EGLint clientCtxVer)
: mConfigId(configId), mClientCtxVer(clientCtxVer) {}
PixelFormat* clone() {
return new EglOsEglPixelFormat(mConfigId, mClientCtxVer);
}
EGLConfig mConfigId;
EGLint mClientCtxVer;
#ifdef __APPLE__
int mRedSize = 0;
int mGreenSize = 0;
int mBlueSize = 0;
#endif // __APPLE__
};
class EglOsEglContext : public EglOS::Context {
public:
EglOsEglContext(EglOsEglDispatcher* dispatcher,
EGLDisplay display,
EGLContext context) :
mDispatcher(dispatcher),
mDisplay(display),
mNativeCtx(context) { }
virtual ~EglOsEglContext() {
D("%s %p\n", __FUNCTION__, mNativeCtx);
if (!mDispatcher->eglDestroyContext(mDisplay, mNativeCtx)) {
// TODO: print a better error message
}
}
EGLContext context() const {
return mNativeCtx;
}
virtual void* getNative() { return (void*)mNativeCtx; }
private:
EglOsEglDispatcher* mDispatcher = nullptr;
EGLDisplay mDisplay;
EGLContext mNativeCtx;
};
class EglOsEglSurface : public EglOS::Surface {
public:
EglOsEglSurface(SurfaceType type,
EGLSurface eglSurface,
EGLNativeWindowType win = 0)
: EglOS::Surface(type), mHndl(eglSurface), mWin(win) {}
EGLSurface getHndl() { return mHndl; }
EGLNativeWindowType getWin() { return mWin; }
private:
EGLSurface mHndl;
EGLNativeWindowType mWin;
};
class EglOsEglDisplay : public EglOS::Display {
public:
EglOsEglDisplay(bool nullEgl);
~EglOsEglDisplay();
virtual EglOS::GlesVersion getMaxGlesVersion();
virtual const char* getExtensionString();
virtual const char* getVendorString();
virtual EGLImage createImageKHR(
EGLDisplay dpy,
EGLContext ctx,
EGLenum target,
EGLClientBuffer buffer,
const EGLint *attrib_list);
virtual EGLBoolean destroyImageKHR(
EGLDisplay dpy,
EGLImage image);
virtual EGLDisplay getNative();
void queryConfigs(int renderableType,
AddConfigCallback* addConfigFunc,
void* addConfigOpaque);
virtual std::shared_ptr<Context>
createContext(EGLint profileMask,
const PixelFormat* pixelFormat,
Context* sharedContext) override;
Surface* createPbufferSurface(const PixelFormat* pixelFormat,
const PbufferInfo* info);
Surface* createWindowSurface(PixelFormat* pf, EGLNativeWindowType win);
bool releasePbuffer(Surface* pb);
bool makeCurrent(Surface* read, Surface* draw, Context* context);
EGLBoolean releaseThread();
void swapBuffers(Surface* srfc);
bool isValidNativeWin(Surface* win);
bool isValidNativeWin(EGLNativeWindowType win);
bool checkWindowPixelFormatMatch(EGLNativeWindowType win,
const PixelFormat* pixelFormat,
unsigned int* width,
unsigned int* height);
void* eglGetProcAddress(const char* func) {
return mDispatcher.eglGetProcAddress(func);
}
EGLint eglDebugMessageControlKHR(EGLDEBUGPROCKHR callback, const EGLAttrib* attribs) {
return mDispatcher.eglDebugMessageControlKHR(callback, attribs);
}
private:
bool mVerbose = false;
EGLDisplay mDisplay;
EglOsEglDispatcher mDispatcher;
bool mHeadless = false;
std::string mClientExts;
std::string mVendor;
GlesVersion mGlesVersion;
#ifdef __linux__
::Display* mGlxDisplay = nullptr;
#endif // __linux__
};
EglOsEglDisplay::EglOsEglDisplay(bool nullEgl) {
mVerbose = android::base::getEnvironmentVariable("ANDROID_EMUGL_VERBOSE") == "1";
if (nullEgl) {
const EGLAttrib attr[] = {
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
EGL_PLATFORM_ANGLE_TYPE_NULL_ANGLE,
EGL_NONE
};
mDisplay = mDispatcher.eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
(void*)EGL_DEFAULT_DISPLAY,
attr);
if (mDisplay == EGL_NO_DISPLAY) {
fprintf(stderr, "%s: no display found that supports null backend\n", __func__);
}
} else if (android::base::getEnvironmentVariable("ANDROID_EMUGL_EXPERIMENTAL_FAST_PATH") == "1") {
const EGLAttrib attr[] = {
EGL_PLATFORM_ANGLE_TYPE_ANGLE,
EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
EGL_EXPERIMENTAL_PRESENT_PATH_ANGLE,
EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE,
EGL_NONE
};
mDisplay = mDispatcher.eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
(void*)EGL_DEFAULT_DISPLAY,
attr);
if (mDisplay == EGL_NO_DISPLAY) {
fprintf(stderr, "%s: no display found that supports the requested extensions\n", __func__);
}
}
else {
mDisplay = mDispatcher.eglGetDisplay(EGL_DEFAULT_DISPLAY);
}
mDispatcher.eglInitialize(mDisplay, nullptr, nullptr);
mDispatcher.eglSwapInterval(mDisplay, 0);
auto clientExts = mDispatcher.eglQueryString(mDisplay, EGL_EXTENSIONS);
auto vendor = mDispatcher.eglQueryString(mDisplay, EGL_VENDOR);
if (mVerbose) {
fprintf(stderr, "%s: client exts: [%s]\n", __func__, clientExts);
}
if (clientExts) {
mClientExts = clientExts;
}
if (vendor) {
mVendor = vendor;
}
mDispatcher.eglBindAPI(EGL_OPENGL_ES_API);
CHECK_EGL_ERR
mHeadless = android::base::getEnvironmentVariable("ANDROID_EMU_HEADLESS") == "1";
#ifdef ANDROID
mGlxDisplay = nullptr;
#elif defined(__linux__)
if (mHeadless) mGlxDisplay = nullptr;
else mGlxDisplay = getX11Api()->XOpenDisplay(0);
#endif // __linux__
if (clientExts != nullptr && emugl::hasExtension(clientExts, "EGL_ANDROID_blob_cache")) {
mDispatcher.eglSetBlobCacheFuncsANDROID(mDisplay, SetBlob, GetBlob);
}
mGlesVersion = GlesVersion::ES2;
static const EGLint gles3ConfigAttribs[] =
{ EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT | EGL_OPENGL_ES3_BIT, EGL_NONE };
static const EGLint pbufAttribs[] =
{ EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
static const EGLint gles31Attribs[] =
{ EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
EGL_CONTEXT_MINOR_VERSION_KHR, 1, EGL_NONE };
static const EGLint gles30Attribs[] =
{ EGL_CONTEXT_MAJOR_VERSION_KHR, 3,
EGL_CONTEXT_MINOR_VERSION_KHR, 0, EGL_NONE };
EGLConfig config;
int numConfigs;
if (mDispatcher.eglChooseConfig(
mDisplay, gles3ConfigAttribs, &config, 1, &numConfigs) &&
numConfigs != 0) {
EGLSurface surface = mDispatcher.eglCreatePbufferSurface(mDisplay,
config, pbufAttribs);
if (surface != EGL_NO_SURFACE) {
EGLContext ctx = mDispatcher.eglCreateContext(mDisplay,
config, EGL_NO_CONTEXT, gles31Attribs);
if (ctx != EGL_NO_CONTEXT) {
mGlesVersion = GlesVersion::ES31;
} else {
ctx = mDispatcher.eglCreateContext(mDisplay, config,
EGL_NO_CONTEXT, gles30Attribs);
if (ctx != EGL_NO_CONTEXT) {
mGlesVersion = GlesVersion::ES30;
}
}
mDispatcher.eglDestroySurface(mDisplay, surface);
if (ctx != EGL_NO_CONTEXT) {
mDispatcher.eglDestroyContext(mDisplay, ctx);
}
}
}
};
EglOsEglDisplay::~EglOsEglDisplay() {
#ifdef ANDROID
#elif defined(__linux__)
if (mGlxDisplay) getX11Api()->XCloseDisplay(mGlxDisplay);
#endif // __linux__
}
EglOS::GlesVersion EglOsEglDisplay::getMaxGlesVersion() {
// Maximum GLES3.1
// GLES3.2 will also need some more autogen + enums if anyone is interested.
return mGlesVersion;
}
const char* EglOsEglDisplay::getExtensionString() {
return mClientExts.c_str();
}
const char* EglOsEglDisplay::getVendorString() {
return mVendor.c_str();
}
EGLImage EglOsEglDisplay::createImageKHR(
EGLDisplay dpy,
EGLContext ctx,
EGLenum target,
EGLClientBuffer buffer,
const EGLint *attrib_list) {
if (mDispatcher.eglCreateImageKHR) {
return mDispatcher.eglCreateImageKHR(dpy, ctx, target, buffer, attrib_list);
} else {
return EGL_NO_IMAGE_KHR;
}
}
EGLBoolean EglOsEglDisplay::destroyImageKHR(
EGLDisplay dpy,
EGLImage image) {
if (mDispatcher.eglDestroyImage) {
return mDispatcher.eglDestroyImageKHR(dpy, image);
} else {
return EGL_FALSE;
}
}
EGLDisplay EglOsEglDisplay::getNative() {
return mDisplay;
}
void EglOsEglDisplay::queryConfigs(int renderableType,
AddConfigCallback* addConfigFunc,
void* addConfigOpaque) {
D("%s\n", __FUNCTION__);
// ANGLE does not support GLES1 uses core profile engine.
// Querying underlying EGL with a conservative set of bits.
renderableType &= ~EGL_OPENGL_ES_BIT;
const EGLint framebuffer_config_attributes[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
EGL_RED_SIZE, 1,
EGL_GREEN_SIZE, 1,
EGL_BLUE_SIZE, 1,
EGL_ALPHA_SIZE, 0,
EGL_NONE,
};
EGLint numConfigs = 0;
mDispatcher.eglChooseConfig(mDisplay, framebuffer_config_attributes, nullptr, 0, &numConfigs);
CHECK_EGL_ERR
std::unique_ptr<EGLConfig[]> configs(new EGLConfig[numConfigs]);
mDispatcher.eglChooseConfig(mDisplay, framebuffer_config_attributes, configs.get(), numConfigs,
&numConfigs);
CHECK_EGL_ERR
if (mVerbose) {
fprintf(stderr, "%s: num configs: %d\n", __func__, numConfigs);
}
for (int i = 0; i < numConfigs; i++) {
const EGLConfig cfg = configs.get()[i];
ConfigInfo configInfo;
// We do not have recordable_android
configInfo.recordable_android = 0;
EGLint _renderableType;
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_RENDERABLE_TYPE,
&_renderableType);
// We do emulate GLES1
configInfo.renderable_type = _renderableType | EGL_OPENGL_ES_BIT;
configInfo.frmt = new EglOsEglPixelFormat(cfg, _renderableType);
D("config %p renderable type 0x%x\n", cfg, _renderableType);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_RED_SIZE,
&configInfo.red_size);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_GREEN_SIZE,
&configInfo.green_size);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_BLUE_SIZE,
&configInfo.blue_size);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_ALPHA_SIZE,
&configInfo.alpha_size);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_CONFIG_CAVEAT,
(EGLint*)&configInfo.caveat);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_DEPTH_SIZE,
&configInfo.depth_size);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_LEVEL,
&configInfo.frame_buffer_level);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_MAX_PBUFFER_WIDTH,
&configInfo.max_pbuffer_width);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_MAX_PBUFFER_HEIGHT,
&configInfo.max_pbuffer_height);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_MAX_PBUFFER_PIXELS,
&configInfo.max_pbuffer_size);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_NATIVE_RENDERABLE,
(EGLint*)&configInfo.native_renderable);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_NATIVE_VISUAL_ID,
&configInfo.native_visual_id);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_NATIVE_VISUAL_TYPE,
&configInfo.native_visual_type);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_SAMPLES,
&configInfo.samples_per_pixel);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_STENCIL_SIZE,
&configInfo.stencil_size);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_SURFACE_TYPE,
&configInfo.surface_type);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_TRANSPARENT_TYPE,
(EGLint*)&configInfo.transparent_type);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg, EGL_TRANSPARENT_RED_VALUE,
&configInfo.trans_red_val);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg,
EGL_TRANSPARENT_GREEN_VALUE,
&configInfo.trans_green_val);
mDispatcher.eglGetConfigAttrib(mDisplay, cfg,
EGL_TRANSPARENT_BLUE_VALUE,
&configInfo.trans_blue_val);
CHECK_EGL_ERR
#ifdef __APPLE__
((EglOsEglPixelFormat*)configInfo.frmt)->mRedSize = configInfo.red_size;
((EglOsEglPixelFormat*)configInfo.frmt)->mGreenSize = configInfo.green_size;
((EglOsEglPixelFormat*)configInfo.frmt)->mBlueSize = configInfo.blue_size;
#endif // __APPLE__
addConfigFunc(addConfigOpaque, &configInfo);
}
D("Host gets %d configs\n", numConfigs);
}
std::shared_ptr<Context>
EglOsEglDisplay::createContext(EGLint profileMask,
const PixelFormat* pixelFormat,
Context* sharedContext) {
(void)profileMask;
D("%s\n", __FUNCTION__);
const EglOsEglPixelFormat* format = (const EglOsEglPixelFormat*)pixelFormat;
D("with config %p\n", format->mConfigId);
// Always GLES3
std::vector<EGLint> attributes = { EGL_CONTEXT_CLIENT_VERSION, 3 };
auto exts = mDispatcher.eglQueryString(mDisplay, EGL_EXTENSIONS);
auto vendor = mDispatcher.eglQueryString(mDisplay, EGL_VENDOR);
// TODO (b/207426737): remove Imagination-specific workaround
bool disable_robustness = vendor && (strcmp(vendor, "Imagination Technologies") == 0);
bool disableValidation = android::base::getEnvironmentVariable("ANDROID_EMUGL_EGL_VALIDATION") == "0";
if (exts != nullptr && emugl::hasExtension(exts, "EGL_KHR_create_context_no_error") && disableValidation) {
attributes.push_back(EGL_CONTEXT_OPENGL_NO_ERROR_KHR);
attributes.push_back(EGL_TRUE);
}
if (exts != nullptr && emugl::hasExtension(exts, "EGL_EXT_create_context_robustness") && !disable_robustness) {
attributes.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
attributes.push_back(EGL_LOSE_CONTEXT_ON_RESET_EXT);
}
attributes.push_back(EGL_NONE);
// TODO: support GLES3.1
EglOsEglContext* nativeSharedCtx = (EglOsEglContext*)sharedContext;
EGLContext newNativeCtx = mDispatcher.eglCreateContext(
mDisplay, format->mConfigId,
nativeSharedCtx ? nativeSharedCtx->context() : nullptr,
attributes.data());
CHECK_EGL_ERR
std::shared_ptr<Context> res =
std::make_shared<EglOsEglContext>(
&mDispatcher, mDisplay, newNativeCtx);
D("%s done\n", __FUNCTION__);
return res;
}
Surface* EglOsEglDisplay::createPbufferSurface(const PixelFormat* pixelFormat,
const PbufferInfo* info) {
// D("%s\n", __FUNCTION__);
// const EglOsEglPixelFormat* format = (const EglOsEglPixelFormat*)pixelFormat;
// EGLint attrib[] = {EGL_WIDTH,
// info->width,
// EGL_HEIGHT,
// info->height,
// EGL_LARGEST_PBUFFER,
// info->largest,
// EGL_TEXTURE_FORMAT,
// info->format,
// EGL_TEXTURE_TARGET,
// info->target,
// EGL_MIPMAP_TEXTURE,
// info->hasMipmap,
// EGL_NONE};
// EGLSurface surface = mDispatcher.eglCreatePbufferSurface(
// mDisplay, format->mConfigId, attrib);
// CHECK_EGL_ERR
// if (surface == EGL_NO_SURFACE) {
// D("create pbuffer surface failed\n");
// return nullptr;
// }
// return new EglOsEglSurface(EglOS::Surface::PBUFFER, surface);
return new EglOsEglSurface(EglOS::Surface::PBUFFER, 0);
}
Surface* EglOsEglDisplay::createWindowSurface(PixelFormat* pf,
EGLNativeWindowType win) {
D("%s\n", __FUNCTION__);
std::vector<EGLint> surface_attribs;
auto exts = mDispatcher.eglQueryString(mDisplay, EGL_EXTENSIONS);
if (exts != nullptr && emugl::hasExtension(exts, "EGL_ANGLE_direct_composition")) {
surface_attribs.push_back(EGL_DIRECT_COMPOSITION_ANGLE);
surface_attribs.push_back(EGL_TRUE);
}
surface_attribs.push_back(EGL_NONE);
EGLSurface surface = mDispatcher.eglCreateWindowSurface(
mDisplay, ((EglOsEglPixelFormat*)pf)->mConfigId, win, surface_attribs.data());
CHECK_EGL_ERR
if (surface == EGL_NO_SURFACE) {
D("create window surface failed\n");
return nullptr;
}
return new EglOsEglSurface(EglOS::Surface::WINDOW, surface, win);
}
bool EglOsEglDisplay::releasePbuffer(Surface* pb) {
D("%s\n", __FUNCTION__);
if (!pb)
return false;
EglOsEglSurface* surface = (EglOsEglSurface*)pb;
if (!surface->getHndl()) {
delete surface;
return true;
}
bool ret = mDispatcher.eglDestroySurface(mDisplay, surface->getHndl());
CHECK_EGL_ERR
D("%s done\n", __FUNCTION__);
delete surface;
return ret;
}
bool EglOsEglDisplay::makeCurrent(Surface* read,
Surface* draw,
Context* context) {
D("%s\n", __FUNCTION__);
EglOsEglSurface* readSfc = (EglOsEglSurface*)read;
EglOsEglSurface* drawSfc = (EglOsEglSurface*)draw;
EglOsEglContext* ctx = (EglOsEglContext*)context;
if (ctx && !readSfc) {
D("warning: makeCurrent a context without surface\n");
return false;
}
D("%s %p\n", __FUNCTION__, ctx ? ctx->context() : nullptr);
bool ret = mDispatcher.eglMakeCurrent(
mDisplay, drawSfc ? drawSfc->getHndl() : EGL_NO_SURFACE,
readSfc ? readSfc->getHndl() : EGL_NO_SURFACE,
ctx ? ctx->context() : EGL_NO_CONTEXT);
if (readSfc) {
D("make current surface type %d %d\n", readSfc->type(),
drawSfc->type());
}
D("make current %d\n", ret);
CHECK_EGL_ERR
return ret;
}
void EglOsEglDisplay::swapBuffers(Surface* surface) {
D("%s\n", __FUNCTION__);
EglOsEglSurface* sfc = (EglOsEglSurface*)surface;
mDispatcher.eglSwapBuffers(mDisplay, sfc->getHndl());
}
EGLBoolean EglOsEglDisplay::releaseThread() {
D("%s\n", __FUNCTION__);
return mDispatcher.eglReleaseThread();
}
bool EglOsEglDisplay::isValidNativeWin(Surface* win) {
if (!win)
return false;
EglOsEglSurface* surface = (EglOsEglSurface*)win;
return surface->type() == EglOsEglSurface::WINDOW &&
isValidNativeWin(surface->getWin());
}
bool EglOsEglDisplay::isValidNativeWin(EGLNativeWindowType win) {
#ifdef _WIN32
return IsWindow(win);
#elif defined(ANDROID)
return true;
#elif defined(__linux__)
Window root;
int t;
unsigned int u;
return getX11Api()->XGetGeometry(mGlxDisplay, win, &root, &t, &t, &u, &u, &u, &u) != 0;
#else // __APPLE__
unsigned int width, height;
return nsGetWinDims(win, &width, &height);
#endif // __APPLE__
}
bool EglOsEglDisplay::checkWindowPixelFormatMatch(EGLNativeWindowType win,
const PixelFormat* pixelFormat,
unsigned int* width,
unsigned int* height) {
#ifdef _WIN32
RECT r;
if (!GetClientRect(win, &r)) {
return false;
}
*width = r.right - r.left;
*height = r.bottom - r.top;
return true;
#elif defined(ANDROID)
*width = ANativeWindow_getWidth((ANativeWindow*)win);
*height = ANativeWindow_getHeight((ANativeWindow*)win);
return true;
#elif defined(__linux__)
//TODO: to check what does ATI & NVIDIA enforce on win pixelformat
unsigned int depth, border;
int x, y;
Window root;
return getX11Api()->XGetGeometry(
mGlxDisplay, win, &root, &x, &y, width, height, &border, &depth);
#else // __APPLE__
bool ret = nsGetWinDims(win, width, height);
const EglOsEglPixelFormat* format = (EglOsEglPixelFormat*)pixelFormat;
int r = format->mRedSize;
int g = format->mGreenSize;
int b = format->mBlueSize;
bool match = nsCheckColor(win, r + g + b);
return ret && match;
#endif // __APPLE__
}
static EglOsEglDisplay* sHostDisplay(bool nullEgl = false) {
static EglOsEglDisplay* d = new EglOsEglDisplay(nullEgl);
return d;
}
class EglEngine : public EglOS::Engine {
public:
EglEngine(bool nullEgl) : EglOS::Engine(), mUseNullEgl(nullEgl) {}
~EglEngine() = default;
EglOS::Display* getDefaultDisplay() {
D("%s\n", __FUNCTION__);
return sHostDisplay(mUseNullEgl);
}
GlLibrary* getGlLibrary() {
D("%s\n", __FUNCTION__);
return &mGlLib;
}
void* eglGetProcAddress(const char* func) {
return sHostDisplay()->eglGetProcAddress(func);
}
virtual EglOS::Surface* createWindowSurface(PixelFormat* pf,
EGLNativeWindowType wnd) {
D("%s\n", __FUNCTION__);
return sHostDisplay()->createWindowSurface(pf, wnd);
}
EGLint eglDebugMessageControlKHR(EGLDEBUGPROCKHR callback, const EGLAttrib* attribs) override {
return sHostDisplay()->eglDebugMessageControlKHR(callback, attribs);
}
private:
EglOsGlLibrary mGlLib;
bool mUseNullEgl;
};
} // namespace
static EglEngine* sHostEngine(bool nullEgl) {
static EglEngine* res = new EglEngine(nullEgl);
return res;
}
namespace EglOS {
Engine* getEgl2EglHostInstance(bool nullEgl) {
D("%s\n", __FUNCTION__);
return sHostEngine(nullEgl);
}
} // namespace EglOS