From cd1bb931979e5a0b9b8f267fa2347bd8d9c28a1b Mon Sep 17 00:00:00 2001 From: Marius Predut Date: Thu, 16 May 2013 15:15:46 +0300 Subject: [PATCH] Added support in the hwc to provide refresh timestamps to the surface flinger. 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 Change-Id: I927e62b5c8c53db859bd194b75f1b92028ea0d85 Signed-off-by: Marius Predut --- Android.mk | 4 +- hwcomposer.cpp | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 160 insertions(+), 7 deletions(-) diff --git a/Android.mk b/Android.mk index 00f22f4..e4d91f0 100644 --- a/Android.mk +++ b/Android.mk @@ -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 diff --git a/hwcomposer.cpp b/hwcomposer.cpp index a3511df..4c4d45d 100644 --- a/hwcomposer.cpp +++ b/hwcomposer.cpp @@ -34,10 +34,12 @@ #define HWC_REMOVE_DEPRECATED_VERSIONS 1 -struct hwc_context_t { - hwc_composer_device_1 device; - struct drm_module_t *gralloc_module; -}; +#include +#include +#include +#include + +#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_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(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; +} + -- 2.11.0