OSDN Git Service

Added support in the hwc to provide refresh timestamps to the surface flinger.
authorMarius Predut <marius-ionut.predut@intel.com>
Thu, 16 May 2013 12:15:46 +0000 (15:15 +0300)
committerMarius Predut <marius-ionut.predut@intel.com>
Thu, 16 May 2013 12:19:44 +0000 (15:19 +0300)
It is done using the same fake mechanism as SF uses. This is prefered until the refresh time stamps will be provides.

v4 (topi):
  - switched upper-case style to underscores
  - remove code parts that should not be part of this patch but future patch
  - update SEC_TO_NANOSEC defined directive and use it in all code.
  - wrong comments style fix.
  - one-line bodies without the brackets,pure whitespace change

v5 (topi/tapani):
  - Indentations fix.
  - compiler warning separate patch
  - include explanation here for 'enabled' and 'disp'
  - added VSYNC missed warning.
  - typo fix.

v6(topi/tapani):
  - 'vsync missed!' message fixed.
  - indentation and newline fixed.

Reviewed-by: Topi Pohjolainen <topi.pohjolainen@intel.com>
Change-Id: I927e62b5c8c53db859bd194b75f1b92028ea0d85
Signed-off-by: Marius Predut <marius-ionut.predut@intel.com>
Android.mk
hwcomposer.cpp

index 00f22f4..e4d91f0 100644 (file)
@@ -17,9 +17,9 @@ LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
-LOCAL_SHARED_LIBRARIES := liblog libEGL libhardware
+LOCAL_SHARED_LIBRARIES := liblog libEGL libhardware libutils libdrm
 LOCAL_CFLAGS := -DEGL_EGLEXT_PROTOTYPES
-LOCAL_C_INCLUDES += bionic hardware/drm_gralloc external/drm external/drm/include/drm
+LOCAL_C_INCLUDES += frameworks/native/include/utils/ hardware/drm_gralloc external/drm external/drm/include/drm
 LOCAL_SRC_FILES := hwcomposer.cpp
 LOCAL_MODULE := hwcomposer.$(TARGET_PRODUCT)
 LOCAL_MODULE_TAGS := optional
index a3511df..4c4d45d 100644 (file)
 
 #define HWC_REMOVE_DEPRECATED_VERSIONS 1
 
-struct hwc_context_t {
-       hwc_composer_device_1 device;
-       struct drm_module_t *gralloc_module;
-};
+#include <Condition.h>
+#include <Mutex.h>
+#include <Thread.h>
+#include <StrongPointer.h>
+
+#define SEC_TO_NANOSEC (1000 * 1000 * 1000)
 
 static int hwc_device_open(const struct hw_module_t* module, const char* name,
                struct hw_device_t** device);
@@ -59,6 +61,62 @@ hwc_module_t HAL_MODULE_INFO_SYM = {
 };
 
 
+/**
+* Fake VSync class.
+* To provide refresh timestamps to the surface flinger, using the
+* same fake mechanism as SF uses on its own, and this is because one
+* cannot start using hwc until it provides certain mandatory things - the
+* refresh time stamps being one of them.
+*/
+class vsync_worker : public android::Thread {
+public:
+       vsync_worker(struct hwc_context_t& hwc);
+       void set_enabled(bool enabled);
+private:
+       virtual void onFirstRef();
+       virtual bool threadLoop();
+       void wait_until_enabled();
+private:
+       struct hwc_context_t& dev;
+       mutable android::Mutex lock;
+       android::Condition condition;
+       bool enabled;
+       mutable int64_t next_fake_vsync;
+       int64_t refresh_period;
+};
+
+struct hwc_context_t {
+       hwc_composer_device_1 device;
+       struct drm_module_t *gralloc_module;
+       hwc_procs_t *procs;
+       android::sp<vsync_worker> vsync_thread;
+};
+
+static void hwc_register_procs(hwc_composer_device_1 *dev,
+                       hwc_procs_t const* procs)
+{
+       struct hwc_context_t* ctx = (struct hwc_context_t*)dev;
+       ctx->procs = (hwc_procs_t *) procs;
+}
+
+static int hwc_event_control(hwc_composer_device_1 *dev, int disp, int event,
+                               int enabled)
+{
+       hwc_context_t* ctx = (hwc_context_t*)dev;
+
+       /**
+        * The API restricts 'enabled' as having boolean values only. Also for
+        * now there can be only one fixed display having identifier zero.
+        */
+       if (event != HWC_EVENT_VSYNC || (enabled != 0 && enabled != 1) || disp)
+               return -EINVAL;
+
+       if (ctx->vsync_thread != NULL)
+               ctx->vsync_thread->set_enabled(enabled);
+
+       return 0;
+}
+
 static int hwc_prepare(hwc_composer_device_1 *dev, size_t numDisplays,
        hwc_display_contents_1_t** displays)
 {
@@ -161,6 +219,9 @@ static int hwc_device_close(struct hw_device_t *dev)
        if (ctx)
                free(ctx);
 
+       if (ctx->vsync_thread != NULL)
+               ctx->vsync_thread->requestExitAndWait();
+
        return 0;
 }
 
@@ -178,7 +239,7 @@ static int hwc_device_open(const struct hw_module_t* module, const char* name,
 
        /* initialize the procs */
        dev->device.common.tag = HARDWARE_DEVICE_TAG;
-       dev->device.common.version = 0;
+       dev->device.common.version = HWC_DEVICE_API_VERSION_1_0;
        dev->device.common.module = const_cast<hw_module_t*>(module);
        dev->device.common.close = hwc_device_close;
 
@@ -187,13 +248,105 @@ static int hwc_device_open(const struct hw_module_t* module, const char* name,
        dev->device.blank = hwc_blank;
        dev->device.getDisplayAttributes = hwc_get_display_attrs;
        dev->device.getDisplayConfigs = hwc_get_display_cfgs;
+       dev->device.registerProcs = hwc_register_procs;
+       dev->device.eventControl = hwc_event_control;
 
        *device = &dev->device.common;
 
        int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
                (const hw_module_t **)&dev->gralloc_module);
 
+       dev->vsync_thread = new vsync_worker(*dev);
+
        ALOGD("Intel hwcomposer module");
 
        return 0;
 }
+
+/* This is needed here as bionic itself is missing the prototype */
+extern "C" int clock_nanosleep(clockid_t clock_id, int flags,
+                       const struct timespec *request, struct timespec *remain);
+
+/**
+ * XXX: this code is temporary and comes from SurfaceFlinger.cpp
+ * so I changed as little as possible since the code will be dropped
+ * anyway, when real functionality will be implemented
+ */
+vsync_worker::vsync_worker(struct hwc_context_t& mydev)
+       : dev(mydev), enabled(false), next_fake_vsync(0)
+{
+       int64_t refresh = 0;
+       framebuffer_device_t*   fbdev;
+
+       int err = framebuffer_open((const hw_module_t *)dev.gralloc_module, &fbdev);
+       if (err)
+               ALOGE("framebuffer_open failed (%s)", strerror(-err));
+       else
+               refresh = int64_t(SEC_TO_NANOSEC / fbdev->fps);
+
+       if (refresh == 0) {
+               refresh = int64_t(SEC_TO_NANOSEC / 60.0);
+               ALOGW("getting VSYNC period from thin air: %lld", refresh);
+       } else
+               ALOGW("getting VSYNC period from fb HAL: %lld", refresh);
+
+       refresh_period = refresh;
+}
+
+void vsync_worker::set_enabled(bool _enabled)
+{
+       android::Mutex::Autolock _l(lock);
+       if (enabled != _enabled) {
+               enabled = _enabled;
+               condition.signal();
+       }
+}
+
+void vsync_worker::wait_until_enabled()
+{
+       android::Mutex::Autolock _l(lock);
+
+       while (!enabled) {
+               condition.wait(lock);
+       }
+}
+
+void vsync_worker::onFirstRef()
+{
+       run("vsync_thread", android::PRIORITY_URGENT_DISPLAY +
+                                       android::PRIORITY_MORE_FAVORABLE);
+}
+
+bool vsync_worker::threadLoop()
+{
+       wait_until_enabled();
+
+       const int64_t now = systemTime(CLOCK_MONOTONIC);
+       int64_t next_vsync = next_fake_vsync;
+       int64_t sleep = next_vsync - now;
+       if (sleep < 0) {
+               /* we missed, find where the next vsync should be */
+               ALOGV("vsync missed!");
+               sleep = (refresh_period - ((now - next_vsync) % refresh_period));
+               next_vsync = now + sleep;
+       }
+
+       next_fake_vsync = next_vsync + refresh_period;
+
+       struct timespec spec;
+       spec.tv_sec  = next_vsync / SEC_TO_NANOSEC;
+       spec.tv_nsec = next_vsync % SEC_TO_NANOSEC;
+
+       int err;
+       do {
+               err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
+       } while (err < 0 && errno == EINTR);
+
+       if (err == 0)
+               dev.procs->vsync(dev.procs, 0, next_vsync);
+       else
+               ALOGE("clock_nanosleep failed with error %d ", err);
+
+       return true;
+}
+