summaryrefslogtreecommitdiffstats
path: root/widget/gonk/libdisplay/GonkDisplayJB.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/gonk/libdisplay/GonkDisplayJB.cpp')
-rw-r--r--widget/gonk/libdisplay/GonkDisplayJB.cpp461
1 files changed, 461 insertions, 0 deletions
diff --git a/widget/gonk/libdisplay/GonkDisplayJB.cpp b/widget/gonk/libdisplay/GonkDisplayJB.cpp
new file mode 100644
index 000000000..197b85a47
--- /dev/null
+++ b/widget/gonk/libdisplay/GonkDisplayJB.cpp
@@ -0,0 +1,461 @@
+/* Copyright 2013 Mozilla Foundation and Mozilla contributors
+ *
+ * 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 "GonkDisplayJB.h"
+#if ANDROID_VERSION == 17
+#include <gui/SurfaceTextureClient.h>
+#else
+#include <gui/Surface.h>
+#include <gui/GraphicBufferAlloc.h>
+#endif
+
+#include <hardware/hardware.h>
+#include <hardware/hwcomposer.h>
+#include <hardware/power.h>
+#include <suspend/autosuspend.h>
+
+#if ANDROID_VERSION >= 19
+#include "VirtualDisplaySurface.h"
+#endif
+#include "FramebufferSurface.h"
+#if ANDROID_VERSION == 17
+#include "GraphicBufferAlloc.h"
+#endif
+#include "mozilla/Assertions.h"
+
+#define DEFAULT_XDPI 75.0
+
+using namespace android;
+
+namespace mozilla {
+
+static GonkDisplayJB* sGonkDisplay = nullptr;
+
+GonkDisplayJB::GonkDisplayJB()
+ : mModule(nullptr)
+ , mFBModule(nullptr)
+ , mHwc(nullptr)
+ , mFBDevice(nullptr)
+ , mPowerModule(nullptr)
+ , mList(nullptr)
+ , mWidth(0)
+ , mHeight(0)
+ , mEnabledCallback(nullptr)
+{
+ int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &mFBModule);
+ ALOGW_IF(err, "%s module not found", GRALLOC_HARDWARE_MODULE_ID);
+ if (!err) {
+ err = framebuffer_open(mFBModule, &mFBDevice);
+ ALOGW_IF(err, "could not open framebuffer");
+ }
+
+ if (!err && mFBDevice) {
+ mWidth = mFBDevice->width;
+ mHeight = mFBDevice->height;
+ xdpi = mFBDevice->xdpi;
+ /* The emulator actually reports RGBA_8888, but EGL doesn't return
+ * any matching configuration. We force RGBX here to fix it. */
+ surfaceformat = HAL_PIXEL_FORMAT_RGBX_8888;
+ }
+
+ err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
+ ALOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
+ if (!err) {
+ err = hwc_open_1(mModule, &mHwc);
+ ALOGE_IF(err, "%s device failed to initialize (%s)",
+ HWC_HARDWARE_COMPOSER, strerror(-err));
+ }
+
+ /* Fallback on the FB rendering path instead of trying to support HWC 1.0 */
+ if (!err && mHwc->common.version == HWC_DEVICE_API_VERSION_1_0) {
+ hwc_close_1(mHwc);
+ mHwc = nullptr;
+ }
+
+ if (!err && mHwc) {
+ if (mFBDevice) {
+ framebuffer_close(mFBDevice);
+ mFBDevice = nullptr;
+ }
+
+ int32_t values[3];
+ const uint32_t attrs[] = {
+ HWC_DISPLAY_WIDTH,
+ HWC_DISPLAY_HEIGHT,
+ HWC_DISPLAY_DPI_X,
+ HWC_DISPLAY_NO_ATTRIBUTE
+ };
+ mHwc->getDisplayAttributes(mHwc, 0, 0, attrs, values);
+
+ mWidth = values[0];
+ mHeight = values[1];
+ xdpi = values[2] / 1000.0f;
+ surfaceformat = HAL_PIXEL_FORMAT_RGBA_8888;
+ }
+
+ err = hw_get_module(POWER_HARDWARE_MODULE_ID,
+ (hw_module_t const**)&mPowerModule);
+ if (!err)
+ mPowerModule->init(mPowerModule);
+ ALOGW_IF(err, "Couldn't load %s module (%s)", POWER_HARDWARE_MODULE_ID, strerror(-err));
+
+ mAlloc = new GraphicBufferAlloc();
+
+ CreateFramebufferSurface(mSTClient, mDispSurface, mWidth, mHeight);
+
+ mList = (hwc_display_contents_1_t *)calloc(1, sizeof(*mList) + (sizeof(hwc_layer_1_t)*2));
+
+ uint32_t usage = GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
+ if (mFBDevice) {
+ // If device uses fb, they can not use single buffer for boot animation
+ mSTClient->perform(mSTClient.get(), NATIVE_WINDOW_SET_BUFFER_COUNT, 2);
+ mSTClient->perform(mSTClient.get(), NATIVE_WINDOW_SET_USAGE, usage);
+ } else if (mHwc) {
+ PowerOnDisplay(HWC_DISPLAY_PRIMARY);
+ // For devices w/ hwc v1.0 or no hwc, this buffer can not be created,
+ // only create this buffer for devices w/ hwc version > 1.0.
+ CreateFramebufferSurface(mBootAnimSTClient, mBootAnimDispSurface, mWidth, mHeight);
+ }
+}
+
+GonkDisplayJB::~GonkDisplayJB()
+{
+ if (mHwc)
+ hwc_close_1(mHwc);
+ if (mFBDevice)
+ framebuffer_close(mFBDevice);
+ free(mList);
+}
+
+void
+GonkDisplayJB::CreateFramebufferSurface(android::sp<ANativeWindow>& aNativeWindow,
+ android::sp<android::DisplaySurface>& aDisplaySurface,
+ uint32_t aWidth,
+ uint32_t aHeight)
+{
+#if ANDROID_VERSION >= 21
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer, mAlloc);
+#elif ANDROID_VERSION >= 19
+ sp<BufferQueue> consumer = new BufferQueue(mAlloc);
+ sp<IGraphicBufferProducer> producer = consumer;
+#elif ANDROID_VERSION >= 18
+ sp<BufferQueue> consumer = new BufferQueue(true, mAlloc);
+ sp<IGraphicBufferProducer> producer = consumer;
+#else
+ sp<BufferQueue> consumer = new BufferQueue(true, mAlloc);
+#endif
+
+ aDisplaySurface = new FramebufferSurface(0, aWidth, aHeight, surfaceformat, consumer);
+
+#if ANDROID_VERSION == 17
+ aNativeWindow = new SurfaceTextureClient(
+ static_cast<sp<ISurfaceTexture>>(aDisplaySurface->getBufferQueue()));
+#else
+ aNativeWindow = new Surface(producer);
+#endif
+}
+
+void
+GonkDisplayJB::CreateVirtualDisplaySurface(android::IGraphicBufferProducer* aSink,
+ android::sp<ANativeWindow>& aNativeWindow,
+ android::sp<android::DisplaySurface>& aDisplaySurface)
+{
+#if ANDROID_VERSION >= 21
+ sp<IGraphicBufferProducer> producer;
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&producer, &consumer, mAlloc);
+#elif ANDROID_VERSION >= 19
+ sp<BufferQueue> consumer = new BufferQueue(mAlloc);
+ sp<IGraphicBufferProducer> producer = consumer;
+#endif
+
+#if ANDROID_VERSION >= 19
+ sp<VirtualDisplaySurface> virtualDisplay;
+ virtualDisplay = new VirtualDisplaySurface(-1, aSink, producer, consumer, String8("VirtualDisplaySurface"));
+ aDisplaySurface = virtualDisplay;
+ aNativeWindow = new Surface(virtualDisplay);
+#endif
+}
+
+void
+GonkDisplayJB::SetEnabled(bool enabled)
+{
+ if (enabled) {
+ autosuspend_disable();
+ mPowerModule->setInteractive(mPowerModule, true);
+ }
+
+ if (!enabled && mEnabledCallback) {
+ mEnabledCallback(enabled);
+ }
+
+#if ANDROID_VERSION >= 21
+ if (mHwc) {
+ if (mHwc->common.version >= HWC_DEVICE_API_VERSION_1_4) {
+ mHwc->setPowerMode(mHwc, HWC_DISPLAY_PRIMARY,
+ (enabled ? HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF));
+ } else {
+ mHwc->blank(mHwc, HWC_DISPLAY_PRIMARY, !enabled);
+ }
+ } else if (mFBDevice && mFBDevice->enableScreen) {
+ mFBDevice->enableScreen(mFBDevice, enabled);
+ }
+#else
+ if (mHwc && mHwc->blank) {
+ mHwc->blank(mHwc, HWC_DISPLAY_PRIMARY, !enabled);
+ } else if (mFBDevice && mFBDevice->enableScreen) {
+ mFBDevice->enableScreen(mFBDevice, enabled);
+ }
+#endif
+
+ if (enabled && mEnabledCallback) {
+ mEnabledCallback(enabled);
+ }
+
+ if (!enabled) {
+ autosuspend_enable();
+ mPowerModule->setInteractive(mPowerModule, false);
+ }
+}
+
+void
+GonkDisplayJB::OnEnabled(OnEnabledCallbackType callback)
+{
+ mEnabledCallback = callback;
+}
+
+void*
+GonkDisplayJB::GetHWCDevice()
+{
+ return mHwc;
+}
+
+bool
+GonkDisplayJB::SwapBuffers(EGLDisplay dpy, EGLSurface sur)
+{
+ // Should be called when composition rendering is complete for a frame.
+ // Only HWC v1.0 needs this call.
+ // HWC > v1.0 case, do not call compositionComplete().
+ // mFBDevice is present only when HWC is v1.0.
+ if (mFBDevice && mFBDevice->compositionComplete) {
+ mFBDevice->compositionComplete(mFBDevice);
+ }
+ return Post(mDispSurface->lastHandle, mDispSurface->GetPrevDispAcquireFd());
+}
+
+bool
+GonkDisplayJB::Post(buffer_handle_t buf, int fence)
+{
+ if (!mHwc) {
+ if (fence >= 0)
+ close(fence);
+ return !mFBDevice->post(mFBDevice, buf);
+ }
+
+ hwc_display_contents_1_t *displays[HWC_NUM_DISPLAY_TYPES] = {NULL};
+ const hwc_rect_t r = { 0, 0, static_cast<int>(mWidth), static_cast<int>(mHeight) };
+ displays[HWC_DISPLAY_PRIMARY] = mList;
+ mList->retireFenceFd = -1;
+ mList->numHwLayers = 2;
+ mList->flags = HWC_GEOMETRY_CHANGED;
+#if ANDROID_VERSION >= 18
+ mList->outbuf = nullptr;
+ mList->outbufAcquireFenceFd = -1;
+#endif
+ mList->hwLayers[0].compositionType = HWC_FRAMEBUFFER;
+ mList->hwLayers[0].hints = 0;
+ /* Skip this layer so the hwc module doesn't complain about null handles */
+ mList->hwLayers[0].flags = HWC_SKIP_LAYER;
+ mList->hwLayers[0].backgroundColor = {0};
+ mList->hwLayers[0].acquireFenceFd = -1;
+ mList->hwLayers[0].releaseFenceFd = -1;
+ /* hwc module checks displayFrame even though it shouldn't */
+ mList->hwLayers[0].displayFrame = r;
+ mList->hwLayers[1].compositionType = HWC_FRAMEBUFFER_TARGET;
+ mList->hwLayers[1].hints = 0;
+ mList->hwLayers[1].flags = 0;
+ mList->hwLayers[1].handle = buf;
+ mList->hwLayers[1].transform = 0;
+ mList->hwLayers[1].blending = HWC_BLENDING_NONE;
+#if ANDROID_VERSION >= 19
+ if (mHwc->common.version >= HWC_DEVICE_API_VERSION_1_3) {
+ mList->hwLayers[1].sourceCropf.left = 0;
+ mList->hwLayers[1].sourceCropf.top = 0;
+ mList->hwLayers[1].sourceCropf.right = mWidth;
+ mList->hwLayers[1].sourceCropf.bottom = mHeight;
+ } else {
+ mList->hwLayers[1].sourceCrop = r;
+ }
+#else
+ mList->hwLayers[1].sourceCrop = r;
+#endif
+ mList->hwLayers[1].displayFrame = r;
+ mList->hwLayers[1].visibleRegionScreen.numRects = 1;
+ mList->hwLayers[1].visibleRegionScreen.rects = &mList->hwLayers[1].displayFrame;
+ mList->hwLayers[1].acquireFenceFd = fence;
+ mList->hwLayers[1].releaseFenceFd = -1;
+#if ANDROID_VERSION >= 18
+ mList->hwLayers[1].planeAlpha = 0xFF;
+#endif
+ mHwc->prepare(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
+ int err = mHwc->set(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
+
+ if (!mBootAnimDispSurface.get()) {
+ mDispSurface->setReleaseFenceFd(mList->hwLayers[1].releaseFenceFd);
+ } else {
+ mBootAnimDispSurface->setReleaseFenceFd(mList->hwLayers[1].releaseFenceFd);
+ }
+
+ if (mList->retireFenceFd >= 0)
+ close(mList->retireFenceFd);
+ return !err;
+}
+
+ANativeWindowBuffer*
+GonkDisplayJB::DequeueBuffer()
+{
+ // Check for bootAnim or normal display flow.
+ sp<ANativeWindow> nativeWindow =
+ !mBootAnimSTClient.get() ? mSTClient : mBootAnimSTClient;
+
+ ANativeWindowBuffer *buf;
+ int fenceFd = -1;
+ nativeWindow->dequeueBuffer(nativeWindow.get(), &buf, &fenceFd);
+ sp<Fence> fence(new Fence(fenceFd));
+#if ANDROID_VERSION == 17
+ fence->waitForever(1000, "GonkDisplayJB_DequeueBuffer");
+ // 1000 is what Android uses. It is a warning timeout in ms.
+ // This timeout was removed in ANDROID_VERSION 18.
+#else
+ fence->waitForever("GonkDisplayJB_DequeueBuffer");
+#endif
+ return buf;
+}
+
+bool
+GonkDisplayJB::QueueBuffer(ANativeWindowBuffer* buf)
+{
+ bool success = false;
+ int error = DoQueueBuffer(buf);
+ // Check for bootAnim or normal display flow.
+ if (!mBootAnimSTClient.get()) {
+ success = Post(mDispSurface->lastHandle, mDispSurface->GetPrevDispAcquireFd());
+ } else {
+ success = Post(mBootAnimDispSurface->lastHandle, mBootAnimDispSurface->GetPrevDispAcquireFd());
+ }
+ return error == 0 && success;
+}
+
+int
+GonkDisplayJB::DoQueueBuffer(ANativeWindowBuffer* buf)
+{
+ int error = 0;
+ // Check for bootAnim or normal display flow.
+ if (!mBootAnimSTClient.get()) {
+ error = mSTClient->queueBuffer(mSTClient.get(), buf, -1);
+ } else {
+ error = mBootAnimSTClient->queueBuffer(mBootAnimSTClient.get(), buf, -1);
+ }
+ return error;
+}
+
+void
+GonkDisplayJB::UpdateDispSurface(EGLDisplay dpy, EGLSurface sur)
+{
+ if (sur != EGL_NO_SURFACE) {
+ eglSwapBuffers(dpy, sur);
+ } else {
+ // When BasicCompositor is used as Compositor,
+ // EGLSurface does not exit.
+ ANativeWindowBuffer* buf = DequeueBuffer();
+ DoQueueBuffer(buf);
+ }
+}
+
+void
+GonkDisplayJB::NotifyBootAnimationStopped()
+{
+ if (mBootAnimSTClient.get()) {
+ mBootAnimSTClient = nullptr;
+ mBootAnimDispSurface = nullptr;
+ }
+}
+
+void
+GonkDisplayJB::PowerOnDisplay(int aDpy)
+{
+ MOZ_ASSERT(mHwc);
+#if ANDROID_VERSION >= 21
+ if (mHwc->common.version >= HWC_DEVICE_API_VERSION_1_4) {
+ mHwc->setPowerMode(mHwc, aDpy, HWC_POWER_MODE_NORMAL);
+ } else {
+ mHwc->blank(mHwc, aDpy, 0);
+ }
+#else
+ mHwc->blank(mHwc, aDpy, 0);
+#endif
+}
+
+GonkDisplay::NativeData
+GonkDisplayJB::GetNativeData(GonkDisplay::DisplayType aDisplayType,
+ android::IGraphicBufferProducer* aSink)
+{
+ NativeData data;
+
+ if (aDisplayType == DISPLAY_PRIMARY) {
+ data.mNativeWindow = mSTClient;
+ data.mDisplaySurface = mDispSurface;
+ data.mXdpi = xdpi;
+ } else if (aDisplayType == DISPLAY_EXTERNAL) {
+ int32_t values[3];
+ const uint32_t attrs[] = {
+ HWC_DISPLAY_WIDTH,
+ HWC_DISPLAY_HEIGHT,
+ HWC_DISPLAY_DPI_X,
+ HWC_DISPLAY_NO_ATTRIBUTE
+ };
+ mHwc->getDisplayAttributes(mHwc, aDisplayType, 0, attrs, values);
+ int width = values[0];
+ int height = values[1];
+ // FIXME!! values[2] returns 0 for external display, which doesn't
+ // sound right, Bug 1169176 is the follow-up bug for this issue.
+ data.mXdpi = values[2] ? values[2] / 1000.f : DEFAULT_XDPI;
+ PowerOnDisplay(HWC_DISPLAY_EXTERNAL);
+ CreateFramebufferSurface(data.mNativeWindow,
+ data.mDisplaySurface,
+ width,
+ height);
+ } else if (aDisplayType == DISPLAY_VIRTUAL) {
+ data.mXdpi = xdpi;
+ CreateVirtualDisplaySurface(aSink,
+ data.mNativeWindow,
+ data.mDisplaySurface);
+ }
+
+ return data;
+}
+
+__attribute__ ((visibility ("default")))
+GonkDisplay*
+GetGonkDisplay()
+{
+ if (!sGonkDisplay)
+ sGonkDisplay = new GonkDisplayJB();
+ return sGonkDisplay;
+}
+
+} // namespace mozilla