blob: d9c761c6db28d8a52fffcd3b418f2eedaa2fbfd6 [file] [log] [blame]
/*
* 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"
#define HWC_REMOVE_DEPRECATED_VERSIONS 1
#include "guest/hals/hwcomposer/hwcomposer.h"
#include <errno.h>
#include <fcntl.h>
#include <math.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sync/sync.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sstream>
#include <string>
#include <vector>
#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <hardware/gralloc.h>
#include <hardware/hardware.h>
#include <hardware/hwcomposer.h>
#include <hardware/hwcomposer_defs.h>
#include <log/log.h>
#include <utils/String8.h>
#include <utils/Vector.h>
#include "guest/hals/hwcomposer/base_composer.h"
#include "guest/hals/hwcomposer/cpu_composer.h"
#include "guest/hals/hwcomposer/geometry_utils.h"
#include "guest/hals/hwcomposer/hwcomposer.h"
#ifdef USE_OLD_HWCOMPOSER
typedef cuttlefish::BaseComposer ComposerType;
#else
typedef cuttlefish::CpuComposer ComposerType;
#endif
struct hwc_composer_device_data_t {
const hwc_procs_t* procs;
pthread_t vsync_thread;
int64_t vsync_base_timestamp;
int32_t vsync_period_ns;
};
struct cvd_hwc_composer_device_1_t {
hwc_composer_device_1_t base;
hwc_composer_device_data_t vsync_data;
cuttlefish::BaseComposer* composer;
};
struct external_display_config_t {
uint64_t physicalId;
uint32_t width;
uint32_t height;
uint32_t dpi;
uint32_t flags;
};
namespace {
void* hwc_vsync_thread(void* data) {
struct hwc_composer_device_data_t* pdev =
(struct hwc_composer_device_data_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) {
LOG_ALWAYS_FATAL("%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;
}
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";
case HWC_SIDEBAND:
return "Sideband";
case HWC_CURSOR_OVERLAY:
return "CursorOverlay";
default:
return std::string("Unknown (") + std::to_string(type) + ")";
}
}
void LogLayers(int num_layers, hwc_layer_1_t* 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(hwc_composer_device_1_t* dev, const hwc_layer_1_t& 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;
}
// 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;
}
auto* cvd_hwc_dev = reinterpret_cast<cvd_hwc_composer_device_1_t*>(dev);
return cvd_hwc_dev->composer->IsValidLayer(layer);
}
bool IsValidComposition(hwc_composer_device_1_t* dev, int num_layers,
hwc_layer_1_t* 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 reasonable 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(dev, 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(dev, layers[idx])) {
ALOGE("%s: Invalid layer found", __FUNCTION__);
LogLayers(num_layers, layers, idx);
return false;
}
break;
}
}
return true;
}
// Note predefined "hwservicemanager." is used to avoid adding new selinux rules
#define EXTERANL_DISPLAY_PROP "hwservicemanager.external.displays"
// return 0 for successful
// return < 0 if failed
int GetExternalDisplayConfigs(std::vector<struct external_display_config_t>* configs) {
// this guest property, hwservicemanager.external.displays,
// specifies multi-display info, with comma (,) as separator
// each display has the following info:
// physicalId,width,height,dpi,flags
// several displays can be provided, e.g., following has 2 displays:
// setprop hwservicemanager.external.displays 1,1200,800,120,0,2,1200,800,120,0
std::vector<uint64_t> values;
char displays_value[PROPERTY_VALUE_MAX] = "";
property_get(EXTERANL_DISPLAY_PROP, displays_value, "");
bool valid = displays_value[0] != '\0';
if (valid) {
char *p = displays_value;
while (*p) {
if (!isdigit(*p) && *p != ',' && *p != ' ') {
valid = false;
break;
}
p++;
}
}
if (!valid) {
// no external displays are specified
ALOGE("%s: Invalid syntax for the value of system prop: %s, value: %s",
__FUNCTION__, EXTERANL_DISPLAY_PROP, displays_value);
return 0;
}
// parse all int values to a vector
std::istringstream stream(displays_value);
for (uint64_t id; stream >> id;) {
values.push_back(id);
if (stream.peek() == ',')
stream.ignore();
}
// each display has 5 values
if ((values.size() % 5) != 0) {
ALOGE("%s: Invalid value for system property: %s", __FUNCTION__, EXTERANL_DISPLAY_PROP);
return -1;
}
while (!values.empty()) {
struct external_display_config_t config;
config.physicalId = values[0];
config.width = values[1];
config.height = values[2];
config.dpi = values[3];
config.flags = values[4];
values.erase(values.begin(), values.begin() + 5);
configs->push_back(config);
}
return 0;
}
} // namespace
static int cvd_hwc_prepare(hwc_composer_device_1_t* dev, size_t numDisplays,
hwc_display_contents_1_t** displays) {
if (!numDisplays || !displays) return 0;
for (int disp = 0; disp < numDisplays; ++disp) {
hwc_display_contents_1_t* list = displays[disp];
if (!list) return 0;
if (!IsValidComposition(dev, list->numHwLayers, &list->hwLayers[0], false)) {
LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
return -1;
}
reinterpret_cast<cvd_hwc_composer_device_1_t*>(dev)->composer->PrepareLayers(
list->numHwLayers, &list->hwLayers[0]);
}
return 0;
}
static int cvd_hwc_set(hwc_composer_device_1_t* dev, size_t numDisplays,
hwc_display_contents_1_t** displays) {
if (!numDisplays || !displays) return 0;
int retval = -1;
for (int disp = 0; disp < numDisplays; ++disp) {
// TODO(b/171305898): Remove the following condition after it supports
// multi-display properly.
if (disp > 0) {
// Skip the composition besides the default display.
break;
}
hwc_display_contents_1_t* contents = displays[disp];
if (!contents) return 0;
hwc_layer_1_t* 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(dev, contents->numHwLayers, layers, true)) {
LOG_ALWAYS_FATAL("%s: Invalid composition requested", __FUNCTION__);
return -1;
}
retval =
reinterpret_cast<cvd_hwc_composer_device_1_t*>(dev)->composer->SetLayers(
contents->numHwLayers, layers);
if (retval != 0) break;
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;
}
static void cvd_hwc_register_procs(hwc_composer_device_1_t* dev,
const hwc_procs_t* procs) {
struct cvd_hwc_composer_device_1_t* pdev =
(struct cvd_hwc_composer_device_1_t*)dev;
pdev->vsync_data.procs = procs;
if (procs) {
std::vector<struct external_display_config_t> configs;
int res = GetExternalDisplayConfigs(&configs);
if (res == 0 && !configs.empty()) {
// configs will be used in the future
procs->hotplug(procs, HWC_DISPLAY_EXTERNAL, 1);
}
}
}
static int cvd_hwc_query(hwc_composer_device_1_t* dev, int what, int* value) {
struct cvd_hwc_composer_device_1_t* pdev =
(struct cvd_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_data.vsync_period_ns;
break;
default:
// unsupported query
ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what);
return -EINVAL;
}
return 0;
}
static int cvd_hwc_event_control(hwc_composer_device_1_t* /*dev*/, int /*dpy*/,
int event, int /*enabled*/) {
if (event == HWC_EVENT_VSYNC) {
return 0;
}
return -EINVAL;
}
static int cvd_hwc_blank(hwc_composer_device_1_t* /*dev*/, int disp, int /*blank*/) {
if (!IS_PRIMARY_DISPLAY(disp) && !IS_EXTERNAL_DISPLAY(disp)) return -EINVAL;
return 0;
}
static void cvd_hwc_dump(hwc_composer_device_1_t* dev, char* buff, int buff_len) {
reinterpret_cast<cvd_hwc_composer_device_1_t*>(dev)->composer->Dump(buff,
buff_len);
}
static int cvd_hwc_get_display_configs(hwc_composer_device_1_t* /*dev*/, int disp,
uint32_t* configs, size_t* numConfigs) {
if (*numConfigs == 0) return 0;
if (IS_PRIMARY_DISPLAY(disp) || IS_EXTERNAL_DISPLAY(disp)) {
configs[0] = 0;
*numConfigs = 1;
return 0;
}
return -EINVAL;
}
static int32_t cvd_hwc_attribute(struct cvd_hwc_composer_device_1_t* pdev,
const uint32_t attribute) {
switch (attribute) {
case HWC_DISPLAY_VSYNC_PERIOD: {
return pdev->vsync_data.vsync_period_ns;
}
case HWC_DISPLAY_WIDTH: {
return cuttlefish::ScreenView::ScreenWidth(0);
}
case HWC_DISPLAY_HEIGHT: {
return cuttlefish::ScreenView::ScreenHeight(0);
}
case HWC_DISPLAY_DPI_X: {
int32_t dpi = cuttlefish::ScreenView::ScreenDPI(0);
ALOGI("Reporting DPI_X of %d", dpi);
// The number of pixels per thousand inches
return dpi * 1000;
}
case HWC_DISPLAY_DPI_Y: {
int32_t dpi = cuttlefish::ScreenView::ScreenDPI(0);
ALOGI("Reporting DPI_Y of %d", dpi);
// The number of pixels per thousand inches
return dpi * 1000;
}
default: {
ALOGE("unknown display attribute %u", attribute);
return -EINVAL;
}
}
}
static int cvd_hwc_get_display_attributes(hwc_composer_device_1_t* dev, int disp,
uint32_t config __unused,
const uint32_t* attributes,
int32_t* values) {
struct cvd_hwc_composer_device_1_t* pdev =
(struct cvd_hwc_composer_device_1_t*)dev;
if (!IS_PRIMARY_DISPLAY(disp) && !IS_EXTERNAL_DISPLAY(disp)) {
ALOGE("unknown display type %u", disp);
return -EINVAL;
}
for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
values[i] = cvd_hwc_attribute(pdev, attributes[i]);
}
return 0;
}
static int cvd_hwc_close(hw_device_t* device) {
struct cvd_hwc_composer_device_1_t* dev =
(struct cvd_hwc_composer_device_1_t*)device;
ALOGE("cvd_hwc_close");
pthread_kill(dev->vsync_data.vsync_thread, SIGTERM);
pthread_join(dev->vsync_data.vsync_thread, NULL);
delete dev->composer;
delete dev;
return 0;
}
namespace cuttlefish {
int cvd_hwc_open(std::unique_ptr<ScreenView> screen_view,
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;
}
cvd_hwc_composer_device_1_t* dev = new cvd_hwc_composer_device_1_t();
if (!dev) {
ALOGE("%s failed to allocate dev", __FUNCTION__);
return -ENOMEM;
}
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_data.vsync_base_timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
dev->vsync_data.vsync_period_ns =
1e9 / cuttlefish::ScreenView::ScreenRefreshRateHz(0);
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 = cvd_hwc_close;
dev->base.prepare = cvd_hwc_prepare;
dev->base.set = cvd_hwc_set;
dev->base.query = cvd_hwc_query;
dev->base.registerProcs = cvd_hwc_register_procs;
dev->base.dump = cvd_hwc_dump;
dev->base.blank = cvd_hwc_blank;
dev->base.eventControl = cvd_hwc_event_control;
dev->base.getDisplayConfigs = cvd_hwc_get_display_configs;
dev->base.getDisplayAttributes = cvd_hwc_get_display_attributes;
#ifdef GATHER_STATS
dev->composer = new cuttlefish::StatsKeepingComposer<ComposerType>(
dev->vsync_data.vsync_base_timestamp, std::move(screen_view));
#else
dev->composer = new ComposerType(std::move(screen_view));
#endif
if (!dev->composer) {
ALOGE("Failed to instantiate the composer object");
delete dev;
return -1;
}
int ret = pthread_create(&dev->vsync_data.vsync_thread, NULL,
hwc_vsync_thread, &dev->vsync_data);
if (ret) {
ALOGE("failed to start vsync thread: %s", strerror(ret));
ret = -ret;
delete dev->composer;
delete dev;
} else {
*device = &dev->base.common;
}
return ret;
}
} // namespace cuttlefish