/* * Copyright (C) 2007 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "DisplayHardware/DisplayHardware.h" #include #include #include #include "GLExtensions.h" using namespace android; static __attribute__((noinline)) void checkGLErrors() { do { // there could be more than one error flag GLenum error = glGetError(); if (error == GL_NO_ERROR) break; LOGE("GL error 0x%04x", int(error)); } while(true); } static __attribute__((noinline)) void checkEGLErrors(const char* token) { EGLint error = eglGetError(); if (error && error != EGL_SUCCESS) { LOGE("%s: EGL error 0x%04x (%s)", token, int(error), EGLUtils::strerror(error)); } } /* * Initialize the display to the specified values. * */ DisplayHardware::DisplayHardware( const sp& flinger, uint32_t dpy) : DisplayHardwareBase(flinger, dpy), mFlags(0) { init(dpy); } DisplayHardware::~DisplayHardware() { fini(); } unsigned int DisplayHardware::getDumpframe() const { return mDumpFrame; } float DisplayHardware::getDpiX() const { return mDpiX; } float DisplayHardware::getDpiY() const { return mDpiY; } float DisplayHardware::getDensity() const { return mDensity; } float DisplayHardware::getRefreshRate() const { return mRefreshRate; } int DisplayHardware::getWidth() const { return mWidth; } int DisplayHardware::getHeight() const { return mHeight; } PixelFormat DisplayHardware::getFormat() const { return mFormat; } uint32_t DisplayHardware::getMaxTextureSize() const { return mMaxTextureSize; } uint32_t DisplayHardware::getMaxViewportDims() const { return mMaxViewportDims; } void DisplayHardware::init(uint32_t dpy) { mNativeWindow = new FramebufferNativeWindow(); framebuffer_device_t const * fbDev = mNativeWindow->getDevice(); mDpiX = mNativeWindow->xdpi; mDpiY = mNativeWindow->ydpi; mRefreshRate = fbDev->fps; mOverlayEngine = NULL; hw_module_t const* module; if (hw_get_module(OVERLAY_HARDWARE_MODULE_ID, &module) == 0) { overlay_control_open(module, &mOverlayEngine); } char property[PROPERTY_VALUE_MAX]; // enable/disable frames dump mDumpFrame=0; if (property_get("debug.dumpframe.enable", property, "0")) { mDumpFrame = atoi(property); } EGLint w, h, dummy; EGLint numConfigs=0; EGLSurface surface; EGLContext context; // initialize EGL EGLint attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE, 0, EGL_NONE }; // debug: disable h/w rendering if (property_get("debug.sf.hw", property, NULL) > 0) { if (atoi(property) == 0) { LOGW("H/W composition disabled"); attribs[2] = EGL_CONFIG_CAVEAT; attribs[3] = EGL_SLOW_CONFIG; mFlags |= CPU_COMPOSITION; } else { // We have hardware composition enabled. Check the composition type if (property_get("debug.composition.type", property, NULL) > 0) { if(((strncmp(property, "c2d", 3)) == 0) || ((strncmp(property, "mdp", 3)) == 0)) { // We wish to use c2d or mdp composition. Try opening copybit if (hw_get_module(COPYBIT_HARDWARE_MODULE_ID, &module) == 0) { copybit_device_t* copybit; copybit_open(module, ©bit); if(copybit) { LOGW("C2D or MDP composition"); mFlags |= (((strncmp(property, "c2d", 3)) == 0)) ? C2D_COMPOSITION:0; attribs[2] = EGL_CONFIG_CAVEAT; attribs[3] = EGL_SLOW_CONFIG; copybit_close(copybit); } } } } } } // TODO: all the extensions below should be queried through // eglGetProcAddress(). EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); eglInitialize(display, NULL, NULL); eglGetConfigs(display, NULL, 0, &numConfigs); EGLConfig config; status_t err = EGLUtils::selectConfigForNativeWindow( display, attribs, mNativeWindow.get(), &config); LOGE_IF(err, "couldn't find an EGLConfig matching the screen format"); EGLint r,g,b,a; eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r); eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g); eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b); eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a); if (mNativeWindow->isUpdateOnDemand()) { mFlags |= PARTIAL_UPDATES; } if (eglGetConfigAttrib(display, config, EGL_CONFIG_CAVEAT, &dummy) == EGL_TRUE) { if (dummy == EGL_SLOW_CONFIG) mFlags |= SLOW_CONFIG; } /* * Create our main surface */ surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL); eglQuerySurface(display, surface, EGL_WIDTH, &mWidth); eglQuerySurface(display, surface, EGL_HEIGHT, &mHeight); #if defined(TARGET_USES_OVERLAY) mOverlayLibObject = new overlay::Overlay(); if(overlay::initOverlay() == -1) LOGE("overlay::initOverlay() ERROR!!"); mOverlayUIEnable = false; mOverlayUIEnablePropVal = false; #if defined(SF_BYPASS) property_get("debug.overlayui.enable", property, "0"); if (atoi(property)) { mOverlayUIEnable = true; mOverlayUIEnablePropVal= true; } #endif #endif if (mFlags & PARTIAL_UPDATES) { // if we have partial updates, we definitely don't need to // preserve the backbuffer, which may be costly. eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED); } if (eglQuerySurface(display, surface, EGL_SWAP_BEHAVIOR, &dummy) == EGL_TRUE) { if (dummy == EGL_BUFFER_PRESERVED) { mFlags |= BUFFER_PRESERVED; } } /* Read density from build-specific ro.sf.lcd_density property * except if it is overridden by qemu.sf.lcd_density. */ if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) { if (property_get("ro.sf.lcd_density", property, NULL) <= 0) { LOGW("ro.sf.lcd_density not defined, using 160 dpi by default."); strcpy(property, "160"); } } else { /* for the emulator case, reset the dpi values too */ mDpiX = mDpiY = atoi(property); } mDensity = atoi(property) * (1.0f/160.0f); /* * Create our OpenGL ES context */ EGLint contextAttributes[] = { #ifdef EGL_IMG_context_priority #ifdef HAS_CONTEXT_PRIORITY #warning "using EGL_IMG_context_priority" EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG, #endif #endif EGL_NONE, EGL_NONE }; context = eglCreateContext(display, config, NULL, contextAttributes); mDisplay = display; mConfig = config; mSurface = surface; mContext = context; mFormat = fbDev->format; mPageFlipCount = 0; /* * Gather OpenGL ES extensions */ eglMakeCurrent(display, surface, surface, context); GLExtensions& extensions(GLExtensions::getInstance()); extensions.initWithGLStrings( glGetString(GL_VENDOR), glGetString(GL_RENDERER), glGetString(GL_VERSION), glGetString(GL_EXTENSIONS), eglQueryString(display, EGL_VENDOR), eglQueryString(display, EGL_VERSION), eglQueryString(display, EGL_EXTENSIONS)); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &mMaxViewportDims); #ifdef EGL_ANDROID_swap_rectangle if (extensions.hasExtension("EGL_ANDROID_swap_rectangle")) { if (eglSetSwapRectangleANDROID(display, surface, 0, 0, mWidth, mHeight) == EGL_TRUE) { // This could fail if this extension is not supported by this // specific surface (of config) mFlags |= SWAP_RECTANGLE; } } // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE // choose PARTIAL_UPDATES, which should be more efficient if (mFlags & PARTIAL_UPDATES) mFlags &= ~SWAP_RECTANGLE; #endif LOGI("EGL informations:"); LOGI("# of configs : %d", numConfigs); LOGI("vendor : %s", extensions.getEglVendor()); LOGI("version : %s", extensions.getEglVersion()); LOGI("extensions: %s", extensions.getEglExtension()); LOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS)?:"Not Supported"); LOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config); LOGI("OpenGL informations:"); LOGI("vendor : %s", extensions.getVendor()); LOGI("renderer : %s", extensions.getRenderer()); LOGI("version : %s", extensions.getVersion()); LOGI("extensions: %s", extensions.getExtension()); LOGI("GL_MAX_TEXTURE_SIZE = %d", mMaxTextureSize); LOGI("GL_MAX_VIEWPORT_DIMS = %d", mMaxViewportDims); LOGI("flags = %08x", mFlags); // Unbind the context from this thread eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); } /* * Clean up. Throw out our local state. * * (It's entirely possible we'll never get here, since this is meant * for real hardware, which doesn't restart.) */ void DisplayHardware::fini() { eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(mDisplay); overlay_control_close(mOverlayEngine); } void DisplayHardware::releaseScreen() const { DisplayHardwareBase::releaseScreen(); } void DisplayHardware::acquireScreen() const { DisplayHardwareBase::acquireScreen(); } uint32_t DisplayHardware::getPageFlipCount() const { return mPageFlipCount; } status_t DisplayHardware::compositionComplete() const { return mNativeWindow->compositionComplete(); } int DisplayHardware::getCurrentBufferIndex() const { return mNativeWindow->getCurrentBufferIndex(); } native_handle_t* DisplayHardware::getCurrentFBHandle() const{ return (native_handle_t*)mNativeWindow->getCurrentBufferHandle(mNativeWindow.get()); } void DisplayHardware::flip(const Region& dirty) const { checkGLErrors(); EGLDisplay dpy = mDisplay; EGLSurface surface = mSurface; #ifdef EGL_ANDROID_swap_rectangle if (mFlags & SWAP_RECTANGLE) { const Region newDirty(dirty.intersect(bounds())); const Rect b(newDirty.getBounds()); eglSetSwapRectangleANDROID(dpy, surface, b.left, b.top, b.width(), b.height()); } #endif if (mFlags & PARTIAL_UPDATES) { mNativeWindow->setUpdateRectangle(dirty.getBounds()); } mPageFlipCount++; eglSwapBuffers(dpy, surface); checkEGLErrors("eglSwapBuffers"); // for debugging //glClearColor(1,0,0,0); //glClear(GL_COLOR_BUFFER_BIT); } status_t DisplayHardware::postBypassBuffer(const native_handle_t* handle, int w, int h, int format, int orientation, int isHPDON) const { framebuffer_device_t *fbDev = (framebuffer_device_t *)mNativeWindow->getDevice(); return fbDev->postBypassBuffer(fbDev, handle, w, h, format, orientation, isHPDON); } status_t DisplayHardware::closeBypass() const { framebuffer_device_t *fbDev = (framebuffer_device_t *)mNativeWindow->getDevice(); return fbDev->closeBypass(fbDev); } status_t DisplayHardware::postOrigResBuffer(const native_handle_t* handle, int w, int h, int format, int orientation ) const { framebuffer_device_t *fbDev = (framebuffer_device_t *)mNativeWindow->getDevice(); return fbDev->postOrigResBuffer(fbDev, handle, w, h, format, orientation); } status_t DisplayHardware::startOrigResDisplay() const { framebuffer_device_t *fbDev = (framebuffer_device_t *)mNativeWindow->getDevice(); return fbDev->startOrigResDisplay(fbDev); } status_t DisplayHardware::stopOrigResDisplay() const { framebuffer_device_t *fbDev = (framebuffer_device_t *)mNativeWindow->getDevice(); return fbDev->stopOrigResDisplay(fbDev); } status_t DisplayHardware::copyBypassBuffer() const { framebuffer_device_t *fbDev = (framebuffer_device_t *)mNativeWindow->getDevice(); return fbDev->copyBypassBuffer(fbDev); } uint32_t DisplayHardware::getFlags() const { return mFlags; } void DisplayHardware::makeCurrent() const { eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); } void DisplayHardware::orientationChanged(int orientation) const { mNativeWindow->orientationChanged(orientation); } void DisplayHardware::videoOverlayStarted(bool started) const { mNativeWindow->videoOverlayStarted(started); } void DisplayHardware::enableHDMIOutput(int enable) const { mNativeWindow->enableHDMIOutput(enable); }