|  | /* | 
|  | ** | 
|  | ** Copyright 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. | 
|  | */ | 
|  |  | 
|  | #define LOG_TAG "FramebufferNativeWindow" | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <errno.h> | 
|  |  | 
|  | #include <cutils/log.h> | 
|  | #include <cutils/atomic.h> | 
|  | #include <utils/threads.h> | 
|  | #include <utils/RefBase.h> | 
|  |  | 
|  | #include <ui/Rect.h> | 
|  | #include <ui/FramebufferNativeWindow.h> | 
|  | #include <ui/GraphicLog.h> | 
|  |  | 
|  | #include <EGL/egl.h> | 
|  |  | 
|  | #include <pixelflinger/format.h> | 
|  | #include <pixelflinger/pixelflinger.h> | 
|  |  | 
|  | #include <hardware/hardware.h> | 
|  | #include <hardware/gralloc.h> | 
|  |  | 
|  | #include <private/ui/android_natives_priv.h> | 
|  |  | 
|  | // ---------------------------------------------------------------------------- | 
|  | namespace android { | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | class NativeBuffer | 
|  | : public EGLNativeBase< | 
|  | android_native_buffer_t, | 
|  | NativeBuffer, | 
|  | LightRefBase<NativeBuffer> > | 
|  | { | 
|  | public: | 
|  | NativeBuffer(int w, int h, int f, int u) : BASE() { | 
|  | android_native_buffer_t::width  = w; | 
|  | android_native_buffer_t::height = h; | 
|  | android_native_buffer_t::format = f; | 
|  | android_native_buffer_t::usage  = u; | 
|  | } | 
|  | private: | 
|  | friend class LightRefBase<NativeBuffer>; | 
|  | ~NativeBuffer() { }; // this class cannot be overloaded | 
|  | }; | 
|  |  | 
|  |  | 
|  | /* | 
|  | * This implements the (main) framebuffer management. This class is used | 
|  | * mostly by SurfaceFlinger, but also by command line GL application. | 
|  | * | 
|  | * In fact this is an implementation of ANativeWindow on top of | 
|  | * the framebuffer. | 
|  | * | 
|  | * Currently it is pretty simple, it manages only two buffers (the front and | 
|  | * back buffer). | 
|  | * | 
|  | */ | 
|  |  | 
|  | FramebufferNativeWindow::FramebufferNativeWindow() | 
|  | : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false) | 
|  | { | 
|  | hw_module_t const* module; | 
|  | if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) { | 
|  | int stride; | 
|  | int err; | 
|  | int i; | 
|  | err = framebuffer_open(module, &fbDev); | 
|  | LOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err)); | 
|  |  | 
|  | err = gralloc_open(module, &grDev); | 
|  | LOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err)); | 
|  |  | 
|  | // bail out if we can't initialize the modules | 
|  | if (!fbDev || !grDev) | 
|  | return; | 
|  |  | 
|  | mUpdateOnDemand = (fbDev->setUpdateRect != 0); | 
|  |  | 
|  | // initialize the buffer FIFO | 
|  | mNumBuffers = NUM_FRAME_BUFFERS; | 
|  | mNumFreeBuffers = NUM_FRAME_BUFFERS; | 
|  | mBufferHead = mNumBuffers-1; | 
|  |  | 
|  | for (i = 0; i < mNumBuffers; i++) | 
|  | { | 
|  | buffers[i] = new NativeBuffer( | 
|  | fbDev->width, fbDev->height, fbDev->format, GRALLOC_USAGE_HW_FB); | 
|  | } | 
|  |  | 
|  | for (i = 0; i < mNumBuffers; i++) | 
|  | { | 
|  | err = grDev->alloc(grDev, | 
|  | fbDev->width, fbDev->height, fbDev->format, | 
|  | GRALLOC_USAGE_HW_FB, &buffers[i]->handle, &buffers[i]->stride); | 
|  |  | 
|  | LOGE_IF(err, "fb buffer %d allocation failed w=%d, h=%d, err=%s", | 
|  | i, fbDev->width, fbDev->height, strerror(-err)); | 
|  |  | 
|  | if (err) | 
|  | { | 
|  | mNumBuffers = i; | 
|  | mNumFreeBuffers = i; | 
|  | mBufferHead = mNumBuffers-1; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | const_cast<uint32_t&>(ANativeWindow::flags) = fbDev->flags; | 
|  | const_cast<float&>(ANativeWindow::xdpi) = fbDev->xdpi; | 
|  | const_cast<float&>(ANativeWindow::ydpi) = fbDev->ydpi; | 
|  | const_cast<int&>(ANativeWindow::minSwapInterval) = | 
|  | fbDev->minSwapInterval; | 
|  | const_cast<int&>(ANativeWindow::maxSwapInterval) = | 
|  | fbDev->maxSwapInterval; | 
|  | } else { | 
|  | LOGE("Couldn't get gralloc module"); | 
|  | } | 
|  |  | 
|  | ANativeWindow::setSwapInterval = setSwapInterval; | 
|  | ANativeWindow::dequeueBuffer = dequeueBuffer; | 
|  | ANativeWindow::lockBuffer = lockBuffer; | 
|  | ANativeWindow::queueBuffer = queueBuffer; | 
|  | ANativeWindow::query = query; | 
|  | ANativeWindow::perform = perform; | 
|  | } | 
|  |  | 
|  | FramebufferNativeWindow::~FramebufferNativeWindow() | 
|  | { | 
|  | if (grDev) { | 
|  | if (buffers[0] != NULL) | 
|  | grDev->free(grDev, buffers[0]->handle); | 
|  | if (buffers[1] != NULL) | 
|  | grDev->free(grDev, buffers[1]->handle); | 
|  | gralloc_close(grDev); | 
|  | } | 
|  |  | 
|  | if (fbDev) { | 
|  | framebuffer_close(fbDev); | 
|  | } | 
|  | } | 
|  |  | 
|  | status_t FramebufferNativeWindow::setUpdateRectangle(const Rect& r) | 
|  | { | 
|  | if (!mUpdateOnDemand) { | 
|  | return INVALID_OPERATION; | 
|  | } | 
|  | return fbDev->setUpdateRect(fbDev, r.left, r.top, r.width(), r.height()); | 
|  | } | 
|  |  | 
|  | status_t FramebufferNativeWindow::compositionComplete() | 
|  | { | 
|  | if (fbDev->compositionComplete) { | 
|  | return fbDev->compositionComplete(fbDev); | 
|  | } | 
|  | return INVALID_OPERATION; | 
|  | } | 
|  |  | 
|  | int FramebufferNativeWindow::setSwapInterval( | 
|  | ANativeWindow* window, int interval) | 
|  | { | 
|  | framebuffer_device_t* fb = getSelf(window)->fbDev; | 
|  | return fb->setSwapInterval(fb, interval); | 
|  | } | 
|  |  | 
|  | void FramebufferNativeWindow::dump(String8& result) { | 
|  | if (fbDev->common.version >= 1 && fbDev->dump) { | 
|  | const size_t SIZE = 4096; | 
|  | char buffer[SIZE]; | 
|  |  | 
|  | fbDev->dump(fbDev, buffer, SIZE); | 
|  | result.append(buffer); | 
|  | } | 
|  | } | 
|  |  | 
|  | // only for debugging / logging | 
|  | int FramebufferNativeWindow::getCurrentBufferIndex() const | 
|  | { | 
|  | Mutex::Autolock _l(mutex); | 
|  | const int index = mCurrentBufferIndex; | 
|  | return index; | 
|  | } | 
|  |  | 
|  | int FramebufferNativeWindow::dequeueBuffer(ANativeWindow* window, | 
|  | android_native_buffer_t** buffer) | 
|  | { | 
|  | FramebufferNativeWindow* self = getSelf(window); | 
|  | Mutex::Autolock _l(self->mutex); | 
|  | framebuffer_device_t* fb = self->fbDev; | 
|  |  | 
|  | int index = self->mBufferHead++; | 
|  | if (self->mBufferHead >= self->mNumBuffers) | 
|  | self->mBufferHead = 0; | 
|  |  | 
|  | GraphicLog& logger(GraphicLog::getInstance()); | 
|  | logger.log(GraphicLog::SF_FB_DEQUEUE_BEFORE, index); | 
|  |  | 
|  | // wait for a free buffer | 
|  | while (!self->mNumFreeBuffers) { | 
|  | self->mCondition.wait(self->mutex); | 
|  | } | 
|  | // get this buffer | 
|  | self->mNumFreeBuffers--; | 
|  | self->mCurrentBufferIndex = index; | 
|  |  | 
|  | *buffer = self->buffers[index].get(); | 
|  |  | 
|  | logger.log(GraphicLog::SF_FB_DEQUEUE_AFTER, index); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int FramebufferNativeWindow::lockBuffer(ANativeWindow* window, | 
|  | android_native_buffer_t* buffer) | 
|  | { | 
|  | FramebufferNativeWindow* self = getSelf(window); | 
|  | Mutex::Autolock _l(self->mutex); | 
|  |  | 
|  | const int index = self->mCurrentBufferIndex; | 
|  | GraphicLog& logger(GraphicLog::getInstance()); | 
|  | logger.log(GraphicLog::SF_FB_LOCK_BEFORE, index); | 
|  |  | 
|  | // wait that the buffer we're locking is not front anymore | 
|  | while (self->front == buffer) { | 
|  | self->mCondition.wait(self->mutex); | 
|  | } | 
|  |  | 
|  | logger.log(GraphicLog::SF_FB_LOCK_AFTER, index); | 
|  |  | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | int FramebufferNativeWindow::queueBuffer(ANativeWindow* window, | 
|  | android_native_buffer_t* buffer) | 
|  | { | 
|  | FramebufferNativeWindow* self = getSelf(window); | 
|  | Mutex::Autolock _l(self->mutex); | 
|  | framebuffer_device_t* fb = self->fbDev; | 
|  | buffer_handle_t handle = static_cast<NativeBuffer*>(buffer)->handle; | 
|  |  | 
|  | const int index = self->mCurrentBufferIndex; | 
|  | GraphicLog& logger(GraphicLog::getInstance()); | 
|  | logger.log(GraphicLog::SF_FB_POST_BEFORE, index); | 
|  |  | 
|  | int res = fb->post(fb, handle); | 
|  |  | 
|  | logger.log(GraphicLog::SF_FB_POST_AFTER, index); | 
|  |  | 
|  | self->front = static_cast<NativeBuffer*>(buffer); | 
|  | self->mNumFreeBuffers++; | 
|  | self->mCondition.broadcast(); | 
|  | return res; | 
|  | } | 
|  |  | 
|  | int FramebufferNativeWindow::query(ANativeWindow* window, | 
|  | int what, int* value) | 
|  | { | 
|  | FramebufferNativeWindow* self = getSelf(window); | 
|  | Mutex::Autolock _l(self->mutex); | 
|  | framebuffer_device_t* fb = self->fbDev; | 
|  | switch (what) { | 
|  | case NATIVE_WINDOW_WIDTH: | 
|  | *value = fb->width; | 
|  | return NO_ERROR; | 
|  | case NATIVE_WINDOW_HEIGHT: | 
|  | *value = fb->height; | 
|  | return NO_ERROR; | 
|  | case NATIVE_WINDOW_FORMAT: | 
|  | *value = fb->format; | 
|  | return NO_ERROR; | 
|  | } | 
|  | *value = 0; | 
|  | return BAD_VALUE; | 
|  | } | 
|  |  | 
|  | int FramebufferNativeWindow::perform(ANativeWindow* window, | 
|  | int operation, ...) | 
|  | { | 
|  | switch (operation) { | 
|  | case NATIVE_WINDOW_SET_USAGE: | 
|  | case NATIVE_WINDOW_CONNECT: | 
|  | case NATIVE_WINDOW_DISCONNECT: | 
|  | break; | 
|  | default: | 
|  | return NAME_NOT_FOUND; | 
|  | } | 
|  | return NO_ERROR; | 
|  | } | 
|  |  | 
|  | // ---------------------------------------------------------------------------- | 
|  | }; // namespace android | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | using namespace android; | 
|  |  | 
|  | EGLNativeWindowType android_createDisplaySurface(void) | 
|  | { | 
|  | FramebufferNativeWindow* w; | 
|  | w = new FramebufferNativeWindow(); | 
|  | if (w->getDevice() == NULL) { | 
|  | // get a ref so it can be destroyed when we exit this block | 
|  | sp<FramebufferNativeWindow> ref(w); | 
|  | return NULL; | 
|  | } | 
|  | return (EGLNativeWindowType)w; | 
|  | } |