| /* |
| * Copyright (C) 2011 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/Lock.h" |
| #include "base/SharedLibrary.h" |
| |
| #include "CoreProfileConfigs.h" |
| #include "GLcommon/GLLibrary.h" |
| |
| #include "apigen-codec-common/ErrorLog.h" |
| |
| #include <string.h> |
| #include <X11/Xlib.h> |
| #include <GL/glx.h> |
| |
| #include <EGL/eglext.h> |
| |
| #include <algorithm> |
| #include <unordered_map> |
| #include <vector> |
| |
| #define DEBUG_PBUF_POOL 0 |
| |
| // TODO: Replace with latency tracker. |
| #define PROFILE_SLOW(tag) |
| |
| namespace { |
| |
| typedef Display X11Display; |
| |
| class ErrorHandler{ |
| public: |
| ErrorHandler(EGLNativeDisplayType dpy); |
| ~ErrorHandler(); |
| int getLastError() const { return s_lastErrorCode; } |
| |
| private: |
| static int s_lastErrorCode; |
| int (*m_oldErrorHandler)(Display *, XErrorEvent *) = nullptr; |
| static android::base::Lock s_lock; |
| static int errorHandlerProc(EGLNativeDisplayType dpy,XErrorEvent* event); |
| }; |
| |
| // static |
| int ErrorHandler::s_lastErrorCode = 0; |
| |
| // static |
| android::base::Lock ErrorHandler::s_lock; |
| |
| ErrorHandler::ErrorHandler(EGLNativeDisplayType dpy) { |
| android::base::AutoLock mutex(s_lock); |
| XSync(dpy,False); |
| s_lastErrorCode = 0; |
| m_oldErrorHandler = XSetErrorHandler(errorHandlerProc); |
| } |
| |
| ErrorHandler::~ErrorHandler() { |
| android::base::AutoLock mutex(s_lock); |
| XSetErrorHandler(m_oldErrorHandler); |
| s_lastErrorCode = 0; |
| } |
| |
| int ErrorHandler::errorHandlerProc(EGLNativeDisplayType dpy, |
| XErrorEvent* event) { |
| s_lastErrorCode = event->error_code; |
| return 0; |
| } |
| |
| #define IS_SUCCESS(a) \ |
| do { if (a != Success) return 0; } while (0) |
| |
| #define EXIT_IF_FALSE(a) \ |
| do { if (a != Success) return; } while (0) |
| |
| class GlxLibrary : public GlLibrary { |
| public: |
| typedef GlFunctionPointer (ResolverFunc)(const char* name); |
| |
| // Important: Use libGL.so.1 explicitly, because it will always link to |
| // the vendor-specific version of the library. libGL.so might in some |
| // cases, depending on bad ldconfig configurations, link to the wrapper |
| // lib that doesn't behave the same. |
| GlxLibrary() { |
| static const char kLibName[] = "libGL.so.1"; |
| char error[256]; |
| mLib = android::base::SharedLibrary::open(kLibName, error, sizeof(error)); |
| if (!mLib) { |
| ERR("%s: Could not open GL library %s [%s]\n", |
| __func__, kLibName, error); |
| return; |
| } |
| // NOTE: Don't use glXGetProcAddress here, only glXGetProcAddressARB |
| // is guaranteed to be supported by vendor-specific libraries. |
| static const char kResolverName[] = "glXGetProcAddressARB"; |
| mResolver = reinterpret_cast<ResolverFunc*>( |
| mLib->findSymbol(kResolverName)); |
| if (!mResolver) { |
| ERR("%s: Could not find resolver %s in %s\n", |
| __func__, kResolverName, kLibName); |
| mLib = NULL; |
| } |
| } |
| |
| ~GlxLibrary() { |
| } |
| |
| // override |
| virtual GlFunctionPointer findSymbol(const char* name) { |
| if (!mLib) { |
| return NULL; |
| } |
| GlFunctionPointer ret = (*mResolver)(name); |
| if (!ret) { |
| ret = reinterpret_cast<GlFunctionPointer>(mLib->findSymbol(name)); |
| } |
| return ret; |
| } |
| |
| private: |
| android::base::SharedLibrary* mLib = nullptr; |
| ResolverFunc* mResolver = nullptr; |
| }; |
| |
| static GlxLibrary* sGlxLibrary() { |
| static GlxLibrary* l = new GlxLibrary; |
| return l; |
| } |
| |
| // Implementation of EglOS::PixelFormat based on GLX. |
| class GlxPixelFormat : public EglOS::PixelFormat { |
| public: |
| explicit GlxPixelFormat(GLXFBConfig fbconfig) : mFbConfig(fbconfig) {} |
| |
| virtual EglOS::PixelFormat* clone() { |
| return new GlxPixelFormat(mFbConfig); |
| } |
| |
| GLXFBConfig fbConfig() const { return mFbConfig; } |
| |
| static GLXFBConfig from(const EglOS::PixelFormat* f) { |
| return static_cast<const GlxPixelFormat*>(f)->fbConfig(); |
| } |
| |
| private: |
| GLXFBConfig mFbConfig = nullptr; |
| }; |
| |
| // Implementation of EglOS::Surface based on GLX. |
| class GlxSurface : public EglOS::Surface { |
| public: |
| GlxSurface(GLXDrawable drawable, GLXFBConfig fbConfig, SurfaceType type) : |
| Surface(type), mFbConfig(fbConfig), mDrawable(drawable) {} |
| |
| GLXDrawable drawable() const { return mDrawable; } |
| GLXFBConfig config() const { return mFbConfig; } |
| |
| // Helper routine to down-cast an EglOS::Surface and extract |
| // its drawable. |
| static GLXDrawable drawableFor(EglOS::Surface* surface) { |
| return static_cast<GlxSurface*>(surface)->drawable(); |
| } |
| |
| // Helper routine to down-cast an EglOS::Surface and extract |
| // its config. |
| static GLXFBConfig configFor(EglOS::Surface* surface) { |
| return static_cast<GlxSurface*>(surface)->config(); |
| } |
| |
| private: |
| GLXFBConfig mFbConfig = 0; |
| GLXDrawable mDrawable = 0; |
| }; |
| |
| void pixelFormatToConfig(EGLNativeDisplayType dpy, |
| int renderableType, |
| GLXFBConfig frmt, |
| EglOS::AddConfigCallback* addConfigFunc, |
| void* addConfigOpaque) { |
| EglOS::ConfigInfo info; |
| int tmp; |
| |
| memset(&info, 0, sizeof(info)); |
| |
| EXIT_IF_FALSE(glXGetFBConfigAttrib(dpy, frmt, GLX_TRANSPARENT_TYPE, &tmp)); |
| if (tmp == GLX_TRANSPARENT_INDEX) { |
| return; // not supporting transparent index |
| } else if (tmp == GLX_NONE) { |
| info.transparent_type = EGL_NONE; |
| info.trans_red_val = 0; |
| info.trans_green_val = 0; |
| info.trans_blue_val = 0; |
| } else { |
| info.transparent_type = EGL_TRANSPARENT_RGB; |
| |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_TRANSPARENT_RED_VALUE, &info.trans_red_val)); |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_TRANSPARENT_GREEN_VALUE, &info.trans_green_val)); |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_TRANSPARENT_BLUE_VALUE, &info.trans_blue_val)); |
| } |
| |
| // |
| // filter out single buffer configurations |
| // |
| int doubleBuffer = 0; |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_DOUBLEBUFFER, &doubleBuffer)); |
| if (!doubleBuffer) { |
| return; |
| } |
| |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy ,frmt, GLX_RED_SIZE, &info.red_size)); |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy ,frmt, GLX_GREEN_SIZE, &info.green_size)); |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy ,frmt, GLX_BLUE_SIZE, &info.blue_size)); |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy ,frmt, GLX_ALPHA_SIZE, &info.alpha_size)); |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy ,frmt, GLX_DEPTH_SIZE, &info.depth_size)); |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy ,frmt, GLX_STENCIL_SIZE, &info.stencil_size)); |
| |
| info.renderable_type = renderableType; |
| int nativeRenderable = 0; |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_X_RENDERABLE, &nativeRenderable)); |
| info.native_renderable = !!nativeRenderable; |
| |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_X_VISUAL_TYPE, &info.native_visual_type)); |
| |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_VISUAL_ID, &info.native_visual_id)); |
| |
| //supported surfaces types |
| info.surface_type = 0; |
| EXIT_IF_FALSE(glXGetFBConfigAttrib(dpy, frmt, GLX_DRAWABLE_TYPE, &tmp)); |
| if (tmp & GLX_WINDOW_BIT && info.native_visual_id != 0) { |
| info.surface_type |= EGL_WINDOW_BIT; |
| } else { |
| info.native_visual_id = 0; |
| info.native_visual_type = EGL_NONE; |
| } |
| if (tmp & GLX_PBUFFER_BIT) { |
| info.surface_type |= EGL_PBUFFER_BIT; |
| } |
| |
| info.caveat = 0; |
| EXIT_IF_FALSE(glXGetFBConfigAttrib(dpy, frmt, GLX_CONFIG_CAVEAT, &tmp)); |
| if (tmp == GLX_NONE) { |
| info.caveat = EGL_NONE; |
| } else if (tmp == GLX_SLOW_CONFIG) { |
| info.caveat = EGL_SLOW_CONFIG; |
| } else if (tmp == GLX_NON_CONFORMANT_CONFIG) { |
| info.caveat = EGL_NON_CONFORMANT_CONFIG; |
| } |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_MAX_PBUFFER_WIDTH, &info.max_pbuffer_width)); |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_MAX_PBUFFER_HEIGHT, &info.max_pbuffer_height)); |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_MAX_PBUFFER_HEIGHT, &info.max_pbuffer_size)); |
| |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_LEVEL, &info.frame_buffer_level)); |
| |
| EXIT_IF_FALSE(glXGetFBConfigAttrib( |
| dpy, frmt, GLX_SAMPLES, &info.samples_per_pixel)); |
| |
| // Filter out configs that do not support RGBA |
| EXIT_IF_FALSE(glXGetFBConfigAttrib(dpy, frmt, GLX_RENDER_TYPE, &tmp)); |
| if (!(tmp & GLX_RGBA_BIT)) { |
| return; |
| } |
| // Filter out configs that do not support depthstencil buffers |
| // For dEQP-GLES2.functional.depth_stencil_clear |
| // and dEQP-GLES2.usecases.* |
| if (info.depth_size == 0 || info.stencil_size == 0) { |
| return; |
| } |
| |
| info.frmt = new GlxPixelFormat(frmt); |
| (*addConfigFunc)(addConfigOpaque, &info); |
| } |
| |
| // Implementation of EglOS::Context based on GLX. |
| class GlxContext : public EglOS::Context { |
| public: |
| explicit GlxContext(X11Display* display, |
| GLXContext context) : |
| mDisplay(display), |
| mContext(context) {} |
| |
| GLXContext context() const { return mContext; } |
| |
| ~GlxContext() { |
| PROFILE_SLOW("~GlxContext()"); |
| glXDestroyContext(mDisplay, mContext); |
| } |
| |
| static GLXContext contextFor(EglOS::Context* context) { |
| return static_cast<GlxContext*>(context)->context(); |
| } |
| private: |
| X11Display* mDisplay = nullptr; |
| GLXContext mContext = nullptr; |
| }; |
| |
| |
| // Implementation of EglOS::Display based on GLX. |
| class GlxDisplay : public EglOS::Display { |
| public: |
| explicit GlxDisplay(X11Display* disp) : mDisplay(disp) {} |
| |
| virtual ~GlxDisplay() { |
| PROFILE_SLOW("displayCleanup"); |
| |
| for (auto it : mLivePbufs) { |
| for (auto surf : it.second) { |
| glXDestroyPbuffer(mDisplay, |
| GlxSurface::drawableFor(surf)); |
| } |
| } |
| |
| for (auto it : mFreePbufs) { |
| for (auto surf : it.second) { |
| glXDestroyPbuffer(mDisplay, |
| GlxSurface::drawableFor(surf)); |
| } |
| } |
| |
| XCloseDisplay(mDisplay); |
| } |
| |
| virtual EglOS::GlesVersion getMaxGlesVersion() { |
| if (!mCoreProfileSupported) { |
| return EglOS::GlesVersion::ES2; |
| } |
| |
| return EglOS::calcMaxESVersionFromCoreVersion( |
| mCoreMajorVersion, mCoreMinorVersion); |
| } |
| |
| virtual void queryConfigs(int renderableType, |
| EglOS::AddConfigCallback* addConfigFunc, |
| void* addConfigOpaque) { |
| int n; |
| GLXFBConfig* frmtList = glXGetFBConfigs(mDisplay, DefaultScreen(mDisplay), &n); |
| if (frmtList) { |
| mFBConfigs.assign(frmtList, frmtList + n); |
| for(int i = 0; i < n; i++) { |
| pixelFormatToConfig( |
| mDisplay, |
| renderableType, |
| frmtList[i], |
| addConfigFunc, |
| addConfigOpaque); |
| } |
| XFree(frmtList); |
| } |
| |
| int glxMaj, glxMin; |
| bool successQueryVersion = |
| glXQueryVersion(mDisplay, |
| &glxMaj, |
| &glxMin); |
| |
| if (successQueryVersion) { |
| if (glxMaj < 1 || (glxMaj >= 1 && glxMin < 4)) { |
| // core profile not supported in this GLX. |
| mCoreProfileSupported = false; |
| } else { |
| queryCoreProfileSupport(); |
| } |
| } else { |
| ERR("%s: Could not query GLX version!\n", __func__); |
| } |
| } |
| |
| virtual bool isValidNativeWin(EglOS::Surface* win) { |
| if (!win) { |
| return false; |
| } else { |
| return isValidNativeWin(GlxSurface::drawableFor(win)); |
| } |
| } |
| |
| virtual bool isValidNativeWin(EGLNativeWindowType win) { |
| Window root; |
| int t; |
| unsigned int u; |
| ErrorHandler handler(mDisplay); |
| if (!XGetGeometry(mDisplay, win, &root, &t, &t, &u, &u, &u, &u)) { |
| return false; |
| } |
| return handler.getLastError() == 0; |
| } |
| |
| virtual bool checkWindowPixelFormatMatch( |
| EGLNativeWindowType win, |
| const EglOS::PixelFormat* pixelFormat, |
| unsigned int* width, |
| unsigned int* height) { |
| //TODO: to check what does ATI & NVIDIA enforce on win pixelformat |
| unsigned int depth, configDepth, border; |
| int r, g, b, x, y; |
| GLXFBConfig fbconfig = GlxPixelFormat::from(pixelFormat); |
| |
| IS_SUCCESS(glXGetFBConfigAttrib( |
| mDisplay, fbconfig, GLX_RED_SIZE, &r)); |
| IS_SUCCESS(glXGetFBConfigAttrib( |
| mDisplay, fbconfig, GLX_GREEN_SIZE, &g)); |
| IS_SUCCESS(glXGetFBConfigAttrib( |
| mDisplay, fbconfig, GLX_BLUE_SIZE, &b)); |
| configDepth = r + g + b; |
| Window root; |
| if (!XGetGeometry( |
| mDisplay, win, &root, &x, &y, width, height, &border, &depth)) { |
| return false; |
| } |
| return depth >= configDepth; |
| } |
| |
| virtual std::shared_ptr<EglOS::Context> createContext( |
| EGLint profileMask, |
| const EglOS::PixelFormat* pixelFormat, |
| EglOS::Context* sharedContext) { |
| PROFILE_SLOW("createContext"); |
| |
| bool useCoreProfile = mCoreProfileSupported && |
| (profileMask & EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR); |
| |
| ErrorHandler handler(mDisplay); |
| |
| GLXContext ctx; |
| if (useCoreProfile) { |
| ctx = mCreateContextAttribs( |
| mDisplay, |
| GlxPixelFormat::from(pixelFormat), |
| sharedContext ? GlxContext::contextFor(sharedContext) : NULL, |
| True /* try direct (supposed to fall back to indirect) */, |
| mCoreProfileCtxAttribs); |
| } else { |
| ctx = glXCreateNewContext( |
| mDisplay, |
| GlxPixelFormat::from(pixelFormat), |
| GLX_RGBA_TYPE, |
| sharedContext ? GlxContext::contextFor(sharedContext) : NULL, |
| true); |
| } |
| |
| if (handler.getLastError()) { |
| return NULL; |
| } |
| |
| return std::make_shared<GlxContext>(mDisplay, ctx); |
| } |
| |
| virtual bool destroyContext(EglOS::Context* context) { |
| PROFILE_SLOW("destroyContext"); |
| glXDestroyContext(mDisplay, GlxContext::contextFor(context)); |
| return true; |
| } |
| |
| void debugCountPbufs(const char* eventName, GLXFBConfig config) { |
| #if DEBUG_PBUF_POOL |
| size_t pbufsFree = 0; |
| size_t pbufsLive = 0; |
| |
| for (const auto it: mFreePbufs) { |
| pbufsFree += it.second.size(); |
| } |
| |
| for (const auto it: mLivePbufs) { |
| pbufsLive += it.second.size(); |
| } |
| |
| fprintf(stderr, "event: %s config: %p Pbuffer counts: free: %zu live: %zu\n", |
| eventName, config, pbufsFree, pbufsLive); |
| #endif |
| } |
| |
| virtual EglOS::Surface* createPbufferSurface( |
| const EglOS::PixelFormat* pixelFormat, |
| const EglOS::PbufferInfo* info) { |
| |
| android::base::AutoLock lock(mPbufLock); |
| |
| GLXFBConfig config = GlxPixelFormat::from(pixelFormat); |
| |
| debugCountPbufs("about to create", config); |
| |
| bool needPrime = false; |
| if (mFreePbufs.find(config) == mFreePbufs.end()) { |
| needPrime = true; |
| } |
| |
| auto& freeElts = mFreePbufs[config]; |
| |
| if (freeElts.size() == 0) { |
| needPrime = true; |
| } |
| |
| if (needPrime) { |
| PROFILE_SLOW("createPbufferSurface (slow path)"); |
| // Double the # pbufs of this config, or create |mPbufPrimingCount| |
| // of them, whichever is higher. |
| int toCreate = std::max((int)mLivePbufs[config].size(), |
| mPbufPrimingCount); |
| for (int i = 0; i < toCreate; i++) { |
| freeElts.push_back(createPbufferSurfaceImpl(config)); |
| } |
| } |
| |
| PROFILE_SLOW("createPbufferSurface (fast path)"); |
| EglOS::Surface* surf = freeElts.back(); |
| freeElts.pop_back(); |
| |
| auto& forLive = mLivePbufs[config]; |
| forLive.push_back(surf); |
| |
| return surf; |
| } |
| |
| virtual bool releasePbuffer(EglOS::Surface* pb) { |
| |
| android::base::AutoLock lock(mPbufLock); |
| |
| PROFILE_SLOW("releasePbuffer"); |
| if (!pb) { |
| return false; |
| } else { |
| GLXFBConfig config = GlxSurface::configFor(pb); |
| |
| debugCountPbufs("about to release", config); |
| |
| auto& frees = mFreePbufs[config]; |
| frees.push_back(pb); |
| |
| auto& forLive = mLivePbufs[config]; |
| forLive.erase(std::remove(forLive.begin(), forLive.end(), pb), forLive.end()); |
| return true; |
| } |
| } |
| |
| virtual bool makeCurrent(EglOS::Surface* read, |
| EglOS::Surface* draw, |
| EglOS::Context* context) { |
| PROFILE_SLOW("makeCurrent"); |
| ErrorHandler handler(mDisplay); |
| bool retval = false; |
| if (!context && !read && !draw) { |
| // unbind |
| retval = glXMakeContextCurrent(mDisplay, 0, 0, NULL); |
| } |
| else if (context && read && draw) { |
| retval = glXMakeContextCurrent( |
| mDisplay, |
| GlxSurface::drawableFor(draw), |
| GlxSurface::drawableFor(read), |
| GlxContext::contextFor(context)); |
| if (mSwapInterval && draw->type() == GlxSurface::SurfaceType::WINDOW) { |
| android::base::AutoLock lock(mPbufLock); |
| auto it = mDisabledVsyncWindows.find(draw); |
| bool notPresent = it == mDisabledVsyncWindows.end(); |
| if (notPresent || !it->second) { |
| mSwapInterval(mDisplay, GlxSurface::drawableFor(draw), 0); |
| if (notPresent) { |
| mDisabledVsyncWindows[draw] = true; |
| } else { |
| it->second = true; |
| } |
| } |
| } |
| } |
| int err = handler.getLastError(); |
| return (err == 0) && retval; |
| } |
| |
| virtual void swapBuffers(EglOS::Surface* srfc) { |
| if (srfc) { |
| glXSwapBuffers(mDisplay, GlxSurface::drawableFor(srfc)); |
| } |
| } |
| |
| private: |
| using CreateContextAttribs = |
| GLXContext (*)(X11Display*, GLXFBConfig, GLXContext, Bool, const int*); |
| using SwapInterval = |
| void (*)(X11Display*, GLXDrawable, int); |
| |
| // Returns the highest level of OpenGL core profile support in |
| // this GLX implementation. |
| void queryCoreProfileSupport() { |
| mCoreProfileSupported = false; |
| ErrorHandler handler(mDisplay); |
| |
| GlxLibrary* lib = sGlxLibrary(); |
| mCreateContextAttribs = |
| (CreateContextAttribs)lib->findSymbol("glXCreateContextAttribsARB"); |
| mSwapInterval = |
| (SwapInterval)lib->findSymbol("glXSwapIntervalEXT"); |
| |
| if (!mCreateContextAttribs || mFBConfigs.size() == 0) return; |
| |
| if (!mSwapInterval) { |
| fprintf(stderr, "%s: swap interval not found\n", __func__); |
| } |
| |
| // Ascending index order of context attribs : |
| // decreasing GL major/minor version |
| GLXContext testContext = nullptr; |
| |
| for (int i = 0; i < getNumCoreProfileCtxAttribs(); i++) { |
| const int* attribs = getCoreProfileCtxAttribs(i); |
| testContext = |
| mCreateContextAttribs( |
| mDisplay, mFBConfigs[0], |
| nullptr /* no shared context */, |
| True /* try direct (supposed to fall back to indirect) */, |
| attribs); |
| |
| if (testContext) { |
| mCoreProfileSupported = true; |
| mCoreProfileCtxAttribs = attribs; |
| getCoreProfileCtxAttribsVersion( |
| attribs, &mCoreMajorVersion, &mCoreMinorVersion); |
| glXDestroyContext(mDisplay, testContext); |
| return; |
| } |
| } |
| } |
| |
| EglOS::Surface* createPbufferSurfaceImpl(GLXFBConfig config) { |
| // we never care about width or height, since we just use |
| // opengl fbos anyway. |
| static const int pbufferImplAttribs[] = { |
| GLX_PBUFFER_WIDTH, 1, |
| GLX_PBUFFER_HEIGHT, 1, |
| GLX_LARGEST_PBUFFER, 0, |
| None |
| }; |
| |
| GLXPbuffer pb; |
| pb = glXCreatePbuffer( |
| mDisplay, |
| config, |
| pbufferImplAttribs); |
| |
| return new GlxSurface(pb, config, GlxSurface::PBUFFER); |
| } |
| |
| CreateContextAttribs mCreateContextAttribs = nullptr; |
| SwapInterval mSwapInterval = nullptr; |
| |
| bool mCoreProfileSupported = false; |
| int mCoreMajorVersion = 4; |
| int mCoreMinorVersion = 5; |
| const int* mCoreProfileCtxAttribs = nullptr; |
| |
| X11Display* mDisplay = nullptr; |
| std::vector<GLXFBConfig> mFBConfigs; |
| std::unordered_map<GLXFBConfig, std::vector<EglOS::Surface* > > mFreePbufs; |
| std::unordered_map<GLXFBConfig, std::vector<EglOS::Surface* > > mLivePbufs; |
| int mPbufPrimingCount = 8; |
| android::base::Lock mPbufLock; |
| std::unordered_map<EglOS::Surface*, bool> mDisabledVsyncWindows; |
| }; |
| |
| class GlxEngine : public EglOS::Engine { |
| public: |
| virtual EglOS::Display* getDefaultDisplay() { |
| Display* disp = |
| XOpenDisplay(0 /* default display or $DISPLAY env var */); |
| if (!disp) { |
| fprintf(stderr, |
| "GlxEngine%s: Failed to open display 0. DISPLAY: [%s]\n", |
| __func__, getenv("DISPLAY")); |
| return nullptr; |
| } |
| return new GlxDisplay(disp); |
| } |
| |
| virtual GlLibrary* getGlLibrary() { |
| return sGlxLibrary(); |
| } |
| |
| virtual EglOS::Surface* createWindowSurface(EglOS::PixelFormat* pf, |
| EGLNativeWindowType wnd) { |
| return new GlxSurface(wnd, 0, GlxSurface::WINDOW); |
| } |
| }; |
| |
| static GlxEngine* sHostEngine() { |
| static GlxEngine* e = new GlxEngine; |
| return e; |
| } |
| |
| } // namespace |
| |
| // static |
| EglOS::Engine* EglOS::Engine::getHostInstance() { |
| return sHostEngine(); |
| } |