/*
* Copyright (C) 2016 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.
*/
// Versions of hwcomposer we implement:
// JB: 0.3
// JB-MR1 to N : 1.1
// N-MR1 to ... : We report 1.1 but SurfaceFlinger has the option to use an
// adapter to treat our 1.1 hwcomposer as a 2.0. If SF stops using that adapter
// to support 1.1 implementations it can be copied into cuttlefish from
// frameworks/native/services/surfaceflinger/DisplayHardware/HWC2On1Adapter.*
#define LOG_TAG "hwc.cf_x86"
#include <guest/libs/platform_support/api_level_fixes.h>
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <string>
#define HWC_REMOVE_DEPRECATED_VERSIONS 1
#include <cutils/compiler.h>
#include <cutils/log.h>
#include <cutils/properties.h>
#include <hardware/gralloc.h>
#include <hardware/hardware.h>
#include <hardware/hwcomposer.h>
#include <hardware/hwcomposer_defs.h>
#include <utils/String8.h>
#include <utils/Vector.h>
#include "common/vsoc/lib/screen_region_view.h"
#include "guest/hals/gralloc/legacy/gralloc_vsoc_priv.h"
#include <sync/sync.h>
#include "base_composer.h"
#include "geometry_utils.h"
#include "hwcomposer_common.h"
#include "stats_keeper.h"
#include "vsoc_composer.h"
using vsoc::screen::ScreenRegionView;
#ifdef USE_OLD_HWCOMPOSER
typedef cvd::BaseComposer InnerComposerType;
#else
typedef cvd::VSoCComposer InnerComposerType;
#endif
#ifdef GATHER_STATS
typedef cvd::StatsKeepingComposer<InnerComposerType> ComposerType;
#else
typedef InnerComposerType ComposerType;
#endif
struct vsoc_hwc_composer_device_1_t {
vsoc_hwc_device base;
const hwc_procs_t* procs;
pthread_t vsync_thread;
int64_t vsync_base_timestamp;
int32_t vsync_period_ns;
ComposerType* composer;
};
namespace {
std::string CompositionString(int type) {
switch (type) {
case HWC_FRAMEBUFFER:
return "Framebuffer";
case HWC_OVERLAY:
return "Overlay";
case HWC_BACKGROUND:
return "Background";
case HWC_FRAMEBUFFER_TARGET:
return "FramebufferTarget";
#if VSOC_PLATFORM_SDK_AFTER(K)
case HWC_SIDEBAND:
return "Sideband";
case HWC_CURSOR_OVERLAY:
return "CursorOverlay";
#endif
default:
return std::string("Unknown (") + std::to_string(type) + ")";
}
}
void LogLayers(int num_layers, vsoc_hwc_layer* layers, int invalid) {
ALOGE("Layers:");
for (int idx = 0; idx < num_layers; ++idx) {
std::string log_line;
if (idx == invalid) {
log_line = "Invalid layer: ";
}
log_line +=
"Composition Type: " + CompositionString(layers[idx].compositionType);
ALOGE("%s", log_line.c_str());
}
}
// Ensures that the layer does not include any inconsistencies
bool IsValidLayer(const vsoc_hwc_layer& layer) {
if (layer.flags & HWC_SKIP_LAYER) {
// A layer we are asked to skip validate should not be marked as skip
ALOGE("%s: Layer is marked as skip", __FUNCTION__);
return false;
}
// 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 false;
}
// Validate the handle
if (private_handle_t::validate(layer.handle) != 0) {
ALOGE("%s: Layer contains an invalid gralloc handle.", __FUNCTION__);
return false;
}
const private_handle_t* p_handle =
reinterpret_cast<const private_handle_t*>(layer.handle);
// 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 false;
}
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 false;
}
return true;
}
bool IsValidComposition(int num_layers, vsoc_hwc_layer* layers, bool on_set) {
if (num_layers == 0) {
ALOGE("Composition requested with 0 layers");
return false;
}
// Sometimes the hwcomposer receives a prepare and set calls with no other
// layer than the FRAMEBUFFER_TARGET with a null handler. We treat this case
// independently as a valid composition, but issue a warning about it.
if (num_layers == 1 && layers[0].compositionType == HWC_FRAMEBUFFER_TARGET &&
layers[0].handle == NULL) {
ALOGW("Received request for empty composition, treating as valid noop");
return true;
}
// The FRAMEBUFFER_TARGET layer needs to be sane only if
// there is at least one layer marked HWC_FRAMEBUFFER or if there is no layer
// marked HWC_OVERLAY (i.e some layers where composed with OpenGL, no layer
// marked overlay or framebuffer means that surfaceflinger decided to go for
// OpenGL without asking the hwcomposer first)
bool check_fb_target = true;
for (int idx = 0; idx < num_layers; ++idx) {
if (layers[idx].compositionType == HWC_FRAMEBUFFER) {
// There is at least one, so it needs to be checked.
// It may have been set to false before, so ensure it's set to true.
check_fb_target = true;
break;
}
if (layers[idx].compositionType == HWC_OVERLAY) {
// At least one overlay, we may not need to.
check_fb_target = false;
}
}
for (int idx = 0; idx < num_layers; ++idx) {
switch (layers[idx].compositionType) {
case HWC_FRAMEBUFFER_TARGET:
// In the call to prepare() the framebuffer target does not have a valid
// buffer_handle, so we don't validate it yet.
if (on_set && check_fb_target && !IsValidLayer(layers[idx])) {
ALOGE("%s: Invalid layer found", __FUNCTION__);
LogLayers(num_layers, layers, idx);
return false;
}
break;
case HWC_OVERLAY:
if (!(layers[idx].flags & HWC_SKIP_LAYER) &&
!IsValidLayer(layers[idx])) {
ALOGE("%s: Invalid layer found", __FUNCTION__);
LogLayers(num_layers, layers, idx);
return false;
}
break;
}
}
return true;
}
}
#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
static int vsoc_hwc_prepare(vsoc_hwc_device* dev, hwc_layer_list_t* list) {
#else
static int vsoc_hwc_prepare(vsoc_hwc_device* 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;
#endif
if (!IsValidComposition(list->numHwLayers, &list->hwLayers[0], false)) {
LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
return -1;
}
reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->PrepareLayers(
list->numHwLayers, &list->hwLayers[0]);
return 0;
}
#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
int vsoc_hwc_set(struct hwc_composer_device* dev, hwc_display_t dpy,
hwc_surface_t sur, hwc_layer_list_t* list) {
if (list->numHwLayers == 1 &&
layers[0].compositionType == HWC_FRAMEBUFFER_TARGET) {
ALOGW("Received request for empty composition, treating as valid noop");
return 0;
}
if (!IsValidComposition(list->numHwLayers, &list->hwLayers[0], true)) {
LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
return -1;
}
return reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)
->composer->SetLayers(list->numHwLayers, &list->hwLayers[0]);
}
#else
static int vsoc_hwc_set(vsoc_hwc_device* dev, size_t numDisplays,
hwc_display_contents_1_t** displays) {
if (!numDisplays || !displays) return 0;
hwc_display_contents_1_t* contents = displays[HWC_DISPLAY_PRIMARY];
if (!contents) return 0;
vsoc_hwc_layer* layers = &contents->hwLayers[0];
if (contents->numHwLayers == 1 &&
layers[0].compositionType == HWC_FRAMEBUFFER_TARGET) {
ALOGW("Received request for empty composition, treating as valid noop");
return 0;
}
if (!IsValidComposition(contents->numHwLayers, layers, true)) {
LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
return -1;
}
int retval =
reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->SetLayers(
contents->numHwLayers, layers);
int closedFds = 0;
for (size_t index = 0; index < contents->numHwLayers; ++index) {
if (layers[index].acquireFenceFd != -1) {
close(layers[index].acquireFenceFd);
layers[index].acquireFenceFd = -1;
++closedFds;
}
}
if (closedFds) {
ALOGI("Saw %zu layers, closed=%d", contents->numHwLayers, closedFds);
}
// TODO(ghartman): This should be set before returning. On the next set it
// should be signalled when we load the new frame.
contents->retireFenceFd = -1;
return retval;
}
#endif
static void vsoc_hwc_register_procs(vsoc_hwc_device* dev,
const hwc_procs_t* procs) {
struct vsoc_hwc_composer_device_1_t* pdev =
(struct vsoc_hwc_composer_device_1_t*)dev;
pdev->procs = procs;
}
static int vsoc_hwc_query(vsoc_hwc_device* dev, int what, int* value) {
struct vsoc_hwc_composer_device_1_t* pdev =
(struct vsoc_hwc_composer_device_1_t*)dev;
switch (what) {
case HWC_BACKGROUND_LAYER_SUPPORTED:
// we support the background layer
value[0] = 0;
break;
case HWC_VSYNC_PERIOD:
value[0] = pdev->vsync_period_ns;
break;
default:
// unsupported query
ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what);
return -EINVAL;
}
return 0;
}
static int vsoc_hwc_event_control(
#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
vsoc_hwc_device* /*dev*/, int event, int /*enabled*/) {
#else
vsoc_hwc_device* /*dev*/, int /*dpy*/, int event, int /*enabled*/) {
#endif
if (event == HWC_EVENT_VSYNC) {
return 0;
}
return -EINVAL;
}
static void* hwc_vsync_thread(void* data) {
struct vsoc_hwc_composer_device_1_t* pdev =
(struct vsoc_hwc_composer_device_1_t*)data;
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 timestamp.
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;
}
static int vsoc_hwc_blank(vsoc_hwc_device* /*dev*/, int disp, int /*blank*/) {
if (!IS_PRIMARY_DISPLAY(disp)) return -EINVAL;
return 0;
}
static void vsoc_hwc_dump(vsoc_hwc_device* dev, char* buff, int buff_len) {
reinterpret_cast<vsoc_hwc_composer_device_1_t*>(dev)->composer->Dump(
buff, buff_len);
}
static int vsoc_hwc_get_display_configs(vsoc_hwc_device* /*dev*/, int disp,
uint32_t* configs, size_t* numConfigs) {
if (*numConfigs == 0) return 0;
if (IS_PRIMARY_DISPLAY(disp)) {
configs[0] = 0;
*numConfigs = 1;
return 0;
}
return -EINVAL;
}
#if VSOC_PLATFORM_SDK_AFTER(J)
static int32_t vsoc_hwc_attribute(struct vsoc_hwc_composer_device_1_t* pdev,
const uint32_t attribute) {
auto screen_view = ScreenRegionView::GetInstance();
switch (attribute) {
case HWC_DISPLAY_VSYNC_PERIOD:
return pdev->vsync_period_ns;
case HWC_DISPLAY_WIDTH:
return screen_view->x_res();
case HWC_DISPLAY_HEIGHT:
return screen_view->y_res();
case HWC_DISPLAY_DPI_X:
ALOGI("Reporting DPI_X of %d", screen_view->dpi());
// The number of pixels per thousand inches
return screen_view->dpi() * 1000;
case HWC_DISPLAY_DPI_Y:
ALOGI("Reporting DPI_Y of %d", screen_view->dpi());
// The number of pixels per thousand inches
return screen_view->dpi() * 1000;
default:
ALOGE("unknown display attribute %u", attribute);
return -EINVAL;
}
}
static int vsoc_hwc_get_display_attributes(vsoc_hwc_device* dev, int disp,
uint32_t config __unused,
const uint32_t* attributes,
int32_t* values) {
struct vsoc_hwc_composer_device_1_t* pdev =
(struct vsoc_hwc_composer_device_1_t*)dev;
if (!IS_PRIMARY_DISPLAY(disp)) {
ALOGE("unknown display type %u", disp);
return -EINVAL;
}
for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
values[i] = vsoc_hwc_attribute(pdev, attributes[i]);
}
return 0;
}
#endif
static int vsoc_hwc_close(hw_device_t* device) {
struct vsoc_hwc_composer_device_1_t* dev =
(struct vsoc_hwc_composer_device_1_t*)device;
ALOGE("vsoc_hwc_close");
pthread_kill(dev->vsync_thread, SIGTERM);
pthread_join(dev->vsync_thread, NULL);
delete dev->composer;
delete dev;
return 0;
}
static int vsoc_hwc_open(const struct hw_module_t* module, const char* name,
struct hw_device_t** device) {
ALOGI("%s", __FUNCTION__);
if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
ALOGE("%s called with bad name %s", __FUNCTION__, name);
return -EINVAL;
}
vsoc_hwc_composer_device_1_t* dev = new vsoc_hwc_composer_device_1_t();
if (!dev) {
ALOGE("%s failed to allocate dev", __FUNCTION__);
return -ENOMEM;
}
int refreshRate = ScreenRegionView::GetInstance()->refresh_rate_hz();
dev->vsync_period_ns = 1000000000 / refreshRate;
struct timespec rt;
if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
ALOGE("%s:%d error in vsync thread 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 = VSOC_HWC_DEVICE_API_VERSION;
dev->base.common.module = const_cast<hw_module_t*>(module);
dev->base.common.close = vsoc_hwc_close;
dev->base.prepare = vsoc_hwc_prepare;
dev->base.set = vsoc_hwc_set;
dev->base.query = vsoc_hwc_query;
dev->base.registerProcs = vsoc_hwc_register_procs;
dev->base.dump = vsoc_hwc_dump;
#if VSOC_PLATFORM_SDK_BEFORE(J_MR1)
static hwc_methods_t hwc_methods = {vsoc_hwc_event_control};
dev->base.methods = &hwc_methods;
#else
dev->base.blank = vsoc_hwc_blank;
dev->base.eventControl = vsoc_hwc_event_control;
dev->base.getDisplayConfigs = vsoc_hwc_get_display_configs;
dev->base.getDisplayAttributes = vsoc_hwc_get_display_attributes;
#endif
dev->composer =
new ComposerType(dev->vsync_base_timestamp, dev->vsync_period_ns);
int ret = pthread_create(&dev->vsync_thread, NULL, hwc_vsync_thread, dev);
if (ret) {
ALOGE("failed to start vsync thread: %s", strerror(ret));
ret = -ret;
delete dev;
} else {
*device = &dev->base.common;
}
return ret;
}
static struct hw_module_methods_t vsoc_hwc_module_methods = {
vsoc_hwc_open,
};
hwc_module_t HAL_MODULE_INFO_SYM = {{HARDWARE_MODULE_TAG,
HWC_MODULE_API_VERSION_0_1,
HARDWARE_HAL_API_VERSION,
HWC_HARDWARE_MODULE_ID,
"VSOC hwcomposer module",
"Google",
&vsoc_hwc_module_methods,
NULL,
{0}}};