/* * Copyright (C) 2017 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 <pthread.h> #include <sys/resource.h> #include <sys/time.h> #include <hardware/hwcomposer.h> #include <hardware/hwcomposer_defs.h> #include <log/log.h> #include "common/vsoc/lib/screen_region_view.h" #include "guest/hals/gralloc/gralloc_vsoc_priv.h" // This file contains just a skeleton hwcomposer, the first step in the // multisided vsoc hwcomposer for cuttlefish. using vsoc::screen::ScreenRegionView; // TODO(jemoreira): ScreenRegionView may become the HWC region namespace { // Ensures that the layer does not include any inconsistencies int SanityCheckLayer(const hwc_layer_1& layer) { // Check displayFrame if (layer.displayFrame.left > layer.displayFrame.right || layer.displayFrame.top > layer.displayFrame.bottom) { ALOGE( "%s: Malformed rectangle (displayFrame): [left = %d, right = %d, top = " "%d, bottom = %d]", __FUNCTION__, layer.displayFrame.left, layer.displayFrame.right, layer.displayFrame.top, layer.displayFrame.bottom); return -EINVAL; } // Check sourceCrop if (layer.sourceCrop.left > layer.sourceCrop.right || layer.sourceCrop.top > layer.sourceCrop.bottom) { ALOGE( "%s: Malformed rectangle (sourceCrop): [left = %d, right = %d, top = " "%d, bottom = %d]", __FUNCTION__, layer.sourceCrop.left, layer.sourceCrop.right, layer.sourceCrop.top, layer.sourceCrop.bottom); return -EINVAL; } const vsoc_buffer_handle_t* p_handle = reinterpret_cast<const vsoc_buffer_handle_t*>(layer.handle); if (!p_handle) { ALOGE("Layer has a NULL buffer handle"); return -EINVAL; } if (layer.sourceCrop.left < 0 || layer.sourceCrop.top < 0 || layer.sourceCrop.right > p_handle->x_res || layer.sourceCrop.bottom > p_handle->y_res) { ALOGE( "%s: Invalid sourceCrop for buffer handle: sourceCrop = [left = %d, " "right = %d, top = %d, bottom = %d], handle = [width = %d, height = " "%d]", __FUNCTION__, layer.sourceCrop.left, layer.sourceCrop.right, layer.sourceCrop.top, layer.sourceCrop.bottom, p_handle->x_res, p_handle->y_res); return -EINVAL; } return 0; } struct vsoc_hwc_device { hwc_composer_device_1_t base; const hwc_procs_t* procs; pthread_t vsync_thread; int64_t vsync_base_timestamp; int32_t vsync_period_ns; uint32_t frame_num; }; void* vsync_thread(void* arg) { vsoc_hwc_device* pdev = reinterpret_cast<vsoc_hwc_device*>(arg); setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); int64_t base_timestamp = pdev->vsync_base_timestamp; int64_t last_logged = base_timestamp / 1e9; int sent = 0; int last_sent = 0; static const int log_interval = 60; void (*vsync_proc)(const struct hwc_procs*, int, int64_t) = nullptr; bool log_no_procs = true, log_no_vsync = true; while (true) { struct timespec rt; if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) { ALOGE("%s:%d error in vsync thread clock_gettime: %s", __FILE__, __LINE__, strerror(errno)); } int64_t timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec; // Given now's timestamp calculate the time of the next vsync. timestamp += pdev->vsync_period_ns - (timestamp - base_timestamp) % pdev->vsync_period_ns; rt.tv_sec = timestamp / 1e9; rt.tv_nsec = timestamp % static_cast<int32_t>(1e9); int err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &rt, NULL); if (err == -1) { ALOGE("error in vsync thread: %s", strerror(errno)); if (errno == EINTR) { continue; } } // The vsync thread is started on device open, it may run before the // registerProcs callback has a chance to be called, so we need to make sure // procs is not NULL before dereferencing it. if (pdev && pdev->procs) { vsync_proc = pdev->procs->vsync; } else if (log_no_procs) { log_no_procs = false; ALOGI("procs is not set yet, unable to deliver vsync event"); } if (vsync_proc) { vsync_proc(const_cast<hwc_procs_t*>(pdev->procs), 0, timestamp); ++sent; } else if (log_no_vsync) { log_no_vsync = false; ALOGE("vsync callback is null (but procs was already set)"); } if (rt.tv_sec - last_logged > log_interval) { ALOGI("Sent %d syncs in %ds", sent - last_sent, log_interval); last_logged = rt.tv_sec; last_sent = sent; } } return NULL; } int hwc_prepare(struct hwc_composer_device_1* /*dev*/, size_t numDisplays, hwc_display_contents_1_t** displays) { if (!numDisplays || !displays) return 0; hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY]; if (!list) return 0; for (size_t i = 0; i < list->numHwLayers; i++) { if (list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) { continue; } list->hwLayers[i].compositionType = HWC_FRAMEBUFFER; } return 0; } int hwc_set(struct hwc_composer_device_1* dev, size_t numDisplays, hwc_display_contents_1_t** displays) { if (!numDisplays || !displays) return 0; hwc_display_contents_1_t* list = displays[HWC_DISPLAY_PRIMARY]; if (!list) return 0; if (!dev) { ALOGE("%s: dev is NULL", __FUNCTION__); return -EINVAL; } for (size_t i = 0; i < list->numHwLayers; i++) { if (vsoc_buffer_handle_t::validate(list->hwLayers[i].handle)) { return -EINVAL; } if (list->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) { if (SanityCheckLayer(list->hwLayers[i])) { ALOGW("Skipping layer %zu due to failed sanity check", i); continue; } const vsoc_buffer_handle_t* fb_handle = reinterpret_cast<const vsoc_buffer_handle_t*>( list->hwLayers[i].handle); ScreenRegionView::GetInstance()->BroadcastNewFrame( fb_handle->offset); break; } } return 0; } int hwc_eventControl(struct hwc_composer_device_1* /*dev*/, int disp, int event, int /*enabled*/) { if (event == HWC_EVENT_VSYNC || disp != HWC_DISPLAY_PRIMARY) { return 0; } return -EINVAL; } int hwc_blank(struct hwc_composer_device_1* /*dev*/, int disp, int /*blank*/) { if (disp != HWC_DISPLAY_PRIMARY) { return -EINVAL; } return 0; } int hwc_query(struct hwc_composer_device_1* dev, int what, int* value) { vsoc_hwc_device* pdev = reinterpret_cast<vsoc_hwc_device*>(dev); switch (what) { case HWC_BACKGROUND_LAYER_SUPPORTED: // we don't support the background layer *value = 0; break; case HWC_VSYNC_PERIOD: *value = pdev->vsync_period_ns; break; case HWC_DISPLAY_TYPES_SUPPORTED: // We only support the primary display *value = HWC_DISPLAY_PRIMARY_BIT; break; default: // unsupported query ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what); return -EINVAL; } return 0; } void hwc_registerProcs(struct hwc_composer_device_1* dev, hwc_procs_t const* procs) { reinterpret_cast<vsoc_hwc_device*>(dev)->procs = procs; } void hwc_dump(struct hwc_composer_device_1* /*dev*/, char* /*buff*/, int /*buff_len*/) {} int hwc_getDisplayConfigs(struct hwc_composer_device_1* /*dev*/, int disp, uint32_t* configs, size_t* numConfigs) { if (*numConfigs == 0) return 0; if (disp == HWC_DISPLAY_PRIMARY) { configs[0] = 0; *numConfigs = 1; return 0; } return -EINVAL; } int32_t vsoc_hwc_attribute(uint32_t attribute) { auto screen_view = ScreenRegionView::GetInstance(); switch (attribute) { case HWC_DISPLAY_VSYNC_PERIOD: return 1000000000 / screen_view->refresh_rate_hz(); case HWC_DISPLAY_WIDTH: return screen_view->x_res(); case HWC_DISPLAY_HEIGHT: return screen_view->y_res(); case HWC_DISPLAY_DPI_X: case HWC_DISPLAY_DPI_Y: // The number of pixels per thousand inches return screen_view->dpi() * 1000; case HWC_DISPLAY_COLOR_TRANSFORM: // TODO(jemoreira): Add the other color transformations return HAL_COLOR_TRANSFORM_IDENTITY; default: ALOGE("unknown display attribute %u", attribute); return -EINVAL; } } int hwc_getDisplayAttributes(struct hwc_composer_device_1* /*dev*/, int disp, uint32_t /*config*/, const uint32_t* attributes, int32_t* values) { if (disp != HWC_DISPLAY_PRIMARY) { ALOGE("Unknown display type %u", disp); return -EINVAL; } for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) { values[i] = vsoc_hwc_attribute(attributes[i]); } return 0; } int hwc_close(hw_device_t* device) { vsoc_hwc_device* dev = reinterpret_cast<vsoc_hwc_device*>(device); pthread_kill(dev->vsync_thread, SIGTERM); pthread_join(dev->vsync_thread, NULL); delete dev; return 0; } int hwc_open(const struct hw_module_t* module, const char* name, struct hw_device_t** device) { ALOGI("Opening vsoc hwcomposer device: %s", __FUNCTION__); if (strcmp(name, HWC_HARDWARE_COMPOSER)) { ALOGE("%s called with bad name %s", __FUNCTION__, name); return -EINVAL; } vsoc_hwc_device* dev = new vsoc_hwc_device(); if (!dev) { ALOGE("%s failed to allocate dev", __FUNCTION__); return -ENOMEM; } memset(dev, 0, sizeof(*dev)); int refreshRate = 60; dev->vsync_period_ns = 1000000000 / refreshRate; struct timespec rt; if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) { ALOGE("%s:%d error in clock_gettime: %s", __FILE__, __LINE__, strerror(errno)); } dev->vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec; dev->base.common.tag = HARDWARE_DEVICE_TAG; dev->base.common.version = HWC_DEVICE_API_VERSION_1_1; dev->base.common.module = const_cast<hw_module_t*>(module); dev->base.common.close = hwc_close; dev->base.prepare = hwc_prepare; dev->base.set = hwc_set; dev->base.query = hwc_query; dev->base.registerProcs = hwc_registerProcs; dev->base.dump = hwc_dump; dev->base.blank = hwc_blank; dev->base.eventControl = hwc_eventControl; dev->base.getDisplayConfigs = hwc_getDisplayConfigs; dev->base.getDisplayAttributes = hwc_getDisplayAttributes; if (!ScreenRegionView::GetInstance()) { ALOGE("Unable to open screen region (%s)", __FUNCTION__); delete dev; return -1; } int ret = pthread_create(&dev->vsync_thread, NULL, vsync_thread, dev); if (ret) { ALOGE("failed to start vsync thread: %s", strerror(ret)); delete dev; } else { *device = &dev->base.common; } return -ret; } struct hw_module_methods_t hwc_module_methods = {hwc_open}; } // namespace hwc_module_t HAL_MODULE_INFO_SYM = {{HARDWARE_MODULE_TAG, HWC_MODULE_API_VERSION_0_1, HARDWARE_HAL_API_VERSION, HWC_HARDWARE_MODULE_ID, "Cuttlefish hwcomposer module", "Google", &hwc_module_methods, NULL, {0}}};