You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1447 lines
56 KiB

/*
* Copyright (c) Hisilicon Technologies Co., Ltd.. 2016-2019. All rights reserved.
* Description: On behalf of a display device, hwcomposer is called for composition and sent for display
* Author: Hisilicon
* Created: 2016.08.12
*/
#define ATRACE_TAG (ATRACE_TAG_GRAPHICS | ATRACE_TAG_HAL)
#include "HWCDisplay.h"
#include <cerrno>
#include <cstdlib>
#include <sstream>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <log/log.h>
#include <utils/Errors.h>
#include <utils/threads.h>
#include <utils/Trace.h>
#include <cutils/properties.h>
#include <securec.h>
#include <linux/fb.h>
#include "uapi_disp.h"
#include "HWCIapiAdapter.h"
#include "VsyncLoopThread.h"
namespace android {
using std::fstream;
using std::string;
using std::to_string;
static int SyncWait(int fd, int timeout)
{
__s32 to = timeout;
return ioctl(fd, SYNC_IOC_WAIT, &to);
}
static bool IsValidPowerMode(const HWC2::PowerMode mode)
{
switch (mode) {
case HWC2::PowerMode::Off: // Fall-through
case HWC2::PowerMode::DozeSuspend: // Fall-through
case HWC2::PowerMode::Doze: // Fall-through
case HWC2::PowerMode::On:
ALOGI("get valid power Mode =%d", mode);
return true;
default:
ALOGI("get invalid power Mode =%d", mode);
return false;
}
}
#ifdef EXT_GFX2D_SUPPORT
void HWCDisplay::QueryFbReleaseFence(const unsigned int phyAddr, int &fbReleaseFd)
{
for (size_t i = 0; i < FENCE_MAP_SIZE; i++) {
if (phyAddr == m_fbReleaseFenceMap[i].phyAddr) {
fbReleaseFd = m_fbReleaseFenceMap[i].releaseFenceid;
// clear fence_map releaseFenceid
m_fbReleaseFenceMap[i].releaseFenceid = HWC_INVALID_FENCE_ID;
break;
}
}
return;
}
void HWCDisplay::AddFbReleaseFence(const unsigned int phyAddr, const int fbReleaseFd)
{
for (size_t i = 0; i < FENCE_MAP_SIZE; i++) {
if (phyAddr == m_fbReleaseFenceMap[i].phyAddr) {
if (m_fbReleaseFenceMap[i].releaseFenceid != HWC_INVALID_FENCE_ID) {
ALOGI("last release fence for addr:0X%x maybe not closed", phyAddr);
}
m_fbReleaseFenceMap[i].releaseFenceid = fbReleaseFd;
// must return do not change break
return;
}
}
for (size_t i = 0; i < FENCE_MAP_SIZE; i++) {
if (m_fbReleaseFenceMap[i].phyAddr == HWC_INVALID_PHY_ADDR) {
m_fbReleaseFenceMap[i].phyAddr = phyAddr;
m_fbReleaseFenceMap[i].releaseFenceid = fbReleaseFd;
break;
}
}
}
#endif
HWCDisplay::HWCDisplay(const DisplayType type, const hwc2_display_t displayId, const framebuffer_device_t *fbDevice,
const bool gfx2dCompose, const HWCCallbacks* callback)
: m_displayType(type),
m_displayFbDevice(fbDevice),
m_layerChanges(),
m_layerRequests(),
m_displayRequests(static_cast<HWC2::DisplayRequest>(0)),
m_displayId(displayId),
m_colorModes(),
m_displayName(),
m_powerMode(HWC2::PowerMode::On),
m_vsyncEnabled(HWC2::Vsync::Invalid),
m_clientTarget({nullptr, -1, -1}),
m_outputBuffer({nullptr, -1, -1}),
m_retireFenceId(-1),
m_lastRetireFenceId(-1),
m_layers(),
m_layerMap(),
m_hwcCallbacks(callback),
m_validated(false),
m_stateMutex(),
m_displayConfigs(),
m_activeConfig(0),
m_activeDisplayConfig({0, 0, 0, 0, 0}),
m_hwcCompose(gfx2dCompose),
m_hwcCompress(false),
m_hwcAsyncCompose(false),
m_lastIonPhyAddr(0),
m_composeType(-1),
m_codeStateFlag(false),
m_frameBufferFd(-1),
m_vsyncMutex(),
m_vsyncCondition(),
m_freshRate(0),
m_vsyncCount(0),
m_supportdirectPresent(false)
{
#ifdef EXT_GFX2D_SUPPORT
for (int i = 0; i < FENCE_MAP_SIZE; i++) {
m_fbReleaseFenceMap[i].phyAddr = HWC_INVALID_PHY_ADDR;
m_fbReleaseFenceMap[i].releaseFenceid = HWC_INVALID_FENCE_ID;
}
#endif
}
HWCDisplay::HWCDisplay(const DisplayType type, const hwc2_display_t displayId, DisplayConfigInfo& displayConfigInfo)
: m_displayType(type),
m_displayFbDevice(nullptr),
m_layerChanges(),
m_layerRequests(),
m_displayRequests(static_cast<HWC2::DisplayRequest>(0)),
m_displayId(displayId),
m_colorModes(),
m_displayName(),
m_powerMode(HWC2::PowerMode::On),
m_vsyncEnabled(HWC2::Vsync::Invalid),
m_clientTarget({nullptr, -1, -1}),
m_outputBuffer({nullptr, -1, -1}),
m_retireFenceId(-1),
m_lastRetireFenceId(-1),
m_layers(),
m_layerMap(),
m_hwcCallbacks(nullptr),
m_validated(false),
m_stateMutex(),
m_displayConfigs(),
m_activeConfig(0),
m_activeDisplayConfig(displayConfigInfo),
m_hwcCompose(true),
m_hwcCompress(false),
m_hwcAsyncCompose(false),
fbCapabilityInfo({}),
m_lastIonPhyAddr(0),
m_composeType(-1),
m_codeStateFlag(false),
m_frameBufferFd(-1),
m_vsyncMutex(),
m_vsyncCondition(),
m_freshRate(0),
m_vsyncCount(0),
m_supportdirectPresent(false)
{
m_displayConfigs.emplace(0, displayConfigInfo);
#ifdef EXT_GFX2D_SUPPORT
for (int i = 0; i < FENCE_MAP_SIZE; i++) {
m_fbReleaseFenceMap[i].phyAddr = HWC_INVALID_PHY_ADDR;
m_fbReleaseFenceMap[i].releaseFenceid = HWC_INVALID_FENCE_ID;
}
#endif
}
void HWCDisplay::Init()
{
if (m_displayFbDevice == nullptr) {
ALOGE("frame buffer device was not initialized");
return;
}
HWCIapiAdapter::GetInstance().InitDisplayConfigs(m_displayId, m_displayConfigs, *m_displayFbDevice,
m_activeConfig);
m_activeDisplayConfig = m_displayConfigs[m_activeConfig];
UpdateVirtualScreenByConfig();
m_hwcCompress = m_hwcCompose && property_get_bool("ro.vendor.gfx.gfx2d.compress", true);
m_supportdirectPresent = property_get_bool("persist.vendor.gfx.hwc.direct_present", false) &&
m_displayType != DisplayType::DISPLAY_EXTERNAL;
if (m_displayType == DisplayType::DISPLAY_PRIMARY || m_displayType == DisplayType::DISPLAY_EXTERNAL) {
private_module_t *module = reinterpret_cast<private_module_t *>(m_displayFbDevice->common.module);
if (module == nullptr) {
ALOGE("fb module was not initialized");
return;
}
int fbDeviceId = HWCIapiAdapter::GetInstance().GetFbDevId(m_displayId);
m_frameBufferFd = module->framebuffer[fbDeviceId]->fd;
ALOGI("fbDeviceId:%d, m_displayId:%" PRIu64 ", m_frameBufferFd:%d", fbDeviceId, m_displayId, m_frameBufferFd);
}
// read the ability of fb
ioctl(m_frameBufferFd, GFBGIOGET_CAPABILITY, &fbCapabilityInfo);
// only support Hardware Vsync for primary display currently
if (m_displayType == DisplayType::DISPLAY_PRIMARY) {
sp<VsyncLoopThread> thread = new VsyncLoopThread(*this);
thread->run("VsyncLoopThread",
static_cast<int>(PRIORITY_URGENT_DISPLAY) + static_cast<int>(PRIORITY_MORE_FAVORABLE));
}
// get debug property
m_fenceMonitorDebug = property_get_bool("vendor.gfx.hwc.debug.fence_monitor", false);
m_fbPixelDebug = property_get_bool("vendor.gfx.hwc.debug.fbbuffer", false);
// the below code is about fb1 attach disp1
if (m_displayType == DisplayType::DISPLAY_PRIMARY) {
return;
}
if (property_get_int32("persist.vendor.disp1.attach.fb", FB_EXTERNAL_ID) != FB_EXTERNAL_CURSOR_ID) {
return;
}
ALOGI("fb1 attach disp1");
gfbg_disp_channel dispChannelTmp = GFBG_DISP_CHN1;
if (ioctl(m_frameBufferFd, GFBGIOSET_CURSOR_ATTACH_CHANNEL, &dispChannelTmp) < 0) {
ALOGE("ioctl GFBGIOSET_CURSOR_ATTACH_CHANNEL failed");
}
}
HWCDisplay::~HWCDisplay()
{
if (m_fbAddr != nullptr) {
const private_handle_t *fbHandle = static_cast<const private_handle_t *>(m_clientTarget.buffer);
// framebuffer count
const size_t fbNum = 3;
const size_t unmapSize = fbHandle->size * fbNum;
munmap(m_fbAddr, unmapSize);
m_fbAddr = nullptr;
}
}
bool HWCDisplay::CanPresentDirectly() const
{
// present layer buffer directly only when ther is only one layer
if (m_supportdirectPresent && (m_layers.size() == 1)) {
const HWCLayer* layer = *m_layers.begin();
const private_handle_t *handle = static_cast<const private_handle_t *>(layer->GetLayerBuffer().buffer);
// read the compression status of buffer
int bufferCompressionFlag = -1;
gralloc_buffer_attr_read(handle, GRALLOC_BUFFER_ATTR_COMPRESSION, &bufferCompressionFlag);
bool support = ((layer->GetLayerSourceCrop().left == 0) &&
(layer->GetLayerSourceCrop().top == 0) &&
(layer->GetLayerSourceCrop().right == static_cast<int>(m_activeDisplayConfig.width)) &&
(layer->GetLayerSourceCrop().bottom == static_cast<int>(m_activeDisplayConfig.height)) &&
(layer->GetDisplayFrame().left == 0) &&
(layer->GetDisplayFrame().top == 0) &&
(layer->GetDisplayFrame().right == static_cast<int>(m_activeDisplayConfig.width)) &&
(layer->GetDisplayFrame().bottom == static_cast<int>(m_activeDisplayConfig.height)) &&
(handle != nullptr) &&
(handle->format == HAL_PIXEL_FORMAT_RGBA_8888) &&
(layer->GetClientRequested() != HWC2::Composition::Sideband) &&
!static_cast<bool>((handle->consumer_usage | handle->producer_usage) &
static_cast<unsigned long long>(GRALLOC_USAGE_PRI_VDP))) &&
(bufferCompressionFlag != COMPRESSION_FLAG_AFBC || fbCapabilityInfo.compression.is_support_afbc);
if (support) {
return true;
}
}
return false;
}
int HWCDisplay::GetComposeType() const
{
HWC_CHK_RETURN((m_displayId == HWC_DISPLAY_VIRTUAL), (int)HWC_COMPOSE::HWC_GPU_COMPOSE);
HWC_CHK_RETURN(property_get_bool(m_lockFb.c_str(), false), (int)HWC_COMPOSE::HWC_GFX2D_COMPOSE);
HWC_CHK_RETURN((CanPresentDirectly()), (int)HWC_COMPOSE::HWC_DIRECT_PRESENT);
HWC_CHK_RETURN(!m_hwcCompose, (int)HWC_COMPOSE::HWC_GPU_COMPOSE);
// num > 7 || num == 0 not support
if (HWCIapiAdapter::GetInstance().CheckLayerNum(m_layers.size())) {
ALOGI("force gpu compose because the number of layers is %zu", m_layers.size());
return (int)HWC_COMPOSE::HWC_GPU_COMPOSE;
}
int resizeNum = 0;
int videoNum = 0;
bool allLayerSideband = true;
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
allLayerSideband = allLayerSideband && (*layer)->GetClientRequested() == HWC2::Composition::Sideband;
if ((*layer)->ForceGpuCompose(resizeNum, videoNum)) {
return (int)HWC_COMPOSE::HWC_GPU_COMPOSE;
}
}
if (allLayerSideband) {
ALOGI("force gpu compose because all layers are composed with sideband");
return (int)HWC_COMPOSE::HWC_GPU_COMPOSE;
}
return (int)HWC_COMPOSE::HWC_GFX2D_COMPOSE;
}
static string ConvertFormat(int fmt)
{
switch (fmt) {
case HAL_PIXEL_FORMAT_RGBA_8888:
return "RGBA_8888";
case HAL_PIXEL_FORMAT_RGBX_8888:
return "RGBX_8888";
case HAL_PIXEL_FORMAT_BGRA_8888:
return "RGBA_8888";
case HAL_PIXEL_FORMAT_RGB_888:
return "RGB_888";
case HAL_PIXEL_FORMAT_RGB_565:
return "RGB_565";
case HAL_PIXEL_FORMAT_RGBA_1010102:
return "RGB_1010102";
case HAL_PIXEL_FORMAT_RGBA_FP16:
return "RGB_FP16";
case HAL_PIXEL_FORMAT_YCbCr_422_I:
return "YCbCr_422_I";
case HAL_PIXEL_FORMAT_YV12:
return "YV12";
case HAL_PIXEL_FORMAT_YCbCr_420_888:
return "YCbCr_420_888";
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
return "YCrCb_420_SP";
case HAL_PIXEL_FORMAT_BLOB:
return "BLOB";
default:
return "Unkonwn";
}
}
void HWCDisplay::CaptureLayerIfNecessary() const
{
static int frame = 0;
const int maxFileNameLen = 100;
// zero values means that capture all layers, otherwize, capture layer with special id
int layerId = property_get_int32("vendor.gfx.hwc.capture.layer", -1);
HWC_CHK_RETURN_NOT_VALUE((layerId < 0));
// capture once default
int frames = property_get_int32("vendor.gfx.hwc.capture.frames", 1);
// set frame to be zero again after finish capture
if (++frame > frames) {
frame = 0;
property_set("vendor.gfx.hwc.capture.layer", "-1");
return;
}
void *virAddr = nullptr;
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
if (layerId != 0 && layerId != static_cast<int>((*layer)->GetId())) {
continue;
}
const private_handle_t *handle = static_cast<const private_handle_t *>((*layer)->GetLayerBuffer().buffer);
if (handle == nullptr) {
ALOGI("Layer%02" PRIu64 " has nullptr handle", (*layer)->GetId());
continue;
}
virAddr = handle->base;
if (virAddr == nullptr) {
ALOGE("Layer%02" PRIu64 " base has nullptr", (*layer)->GetId());
continue;
}
char fname[maxFileNameLen] = {0};
int ret = snprintf_s(fname, maxFileNameLen, maxFileNameLen - 1,
"/data/hwc_frame%02d_Layer%02" PRIu64 "_%dx%d_%s", frame, (*layer)->GetId(), handle->width,
handle->height, ConvertFormat(handle->format).c_str());
HWC_CHK_RETURN_NOT_VALUE((ret < 0), ALOGE("snprintf_s fname failed(0x%x)", ret));
FILE *fp = fopen(fname, "wb");
HWC_CHK_RETURN_NOT_VALUE((fp == nullptr), ALOGE("fopen %s failed", fname));
if (fchmod(fileno(fp), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) != 0) {
ALOGE("fchmod %s failed", fname);
fclose(fp);
fp = nullptr;
return;
}
if ((*layer)->GetLayerBuffer().fenceId != HWC_INVALID_FENCE_ID) {
int err = SyncWait((*layer)->GetLayerBuffer().fenceId, HWC_FENCE_TIMEOUT);
if (err < 0 && errno == ETIME) {
ALOGE("HWC sync_wait for over 3s %s,%d", __FUNCTION__, __LINE__);
}
}
size_t hasWriten = fwrite(virAddr, sizeof(unsigned char), handle->size, fp);
ALOGI("fwrite %zu bytes from virtual addr into %s with bytes_stride:%d",
hasWriten, fname, handle->bytes_stride);
fclose(fp);
}
}
void HWCDisplay::WaitIfNecessary() const
{
if (property_get_bool("vendor.gfx.hwc.wait.fence", false)) {
int64_t start = systemTime(SYSTEM_TIME_MONOTONIC) / 1000LL;
SyncWait(m_clientTarget.fenceId, HWC_FENCE_TIMEOUT);
ALOGE("sync_wait clientTarget fence:%d for %" PRId64 " us", m_clientTarget.fenceId,
systemTime(SYSTEM_TIME_MONOTONIC) / 1000 - start);
}
int32_t moreTime = property_get_int32("vendor.gfx.hwc.wait", 0);
if (moreTime > 0) {
usleep(moreTime);
ALOGE("wait moreTime %d", moreTime);
}
}
void HWCDisplay::HwcMediaFresh()
{
// Set Sideband Position
int zorder;
if (HWCIapiAdapter::GetInstance().GetFbDevId(m_displayId) == FB_PRIMARY_ID) {
zorder = 0;
} else {
zorder = INT_MIN; // don't support zorder for UAPI_DISPLAY1
}
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
// set sideband window position
if ((*layer)->GetDeviceSelected() == HWC2::Composition::Sideband) {
const native_handle_t *sideband = (*layer)->GetLayerSidebandStream();
const uint16_t absZorderOffset = 1;
if (sideband != nullptr) {
HWCIapiAdapter::GetInstance().SetVoWindowPosition(*sideband, (*layer)->GetDisplayFrame(),
zorder, (*layer)->GetLayerZOrder() + absZorderOffset);
zorder++;
}
}
}
}
int HWCDisplay::HwcDirectFresh()
{
HWCLayer* layer = *m_layers.begin();
// record last fb lease fence;
m_lastRetireFenceId = m_retireFenceId;
m_retireFenceId = HWC_INVALID_FENCE_ID;
HWC_CHK_RETURN((layer == nullptr), HWC_FAILURE, ALOGE("HwcDirectFresh layer is null"));
HWCBuffer &buffer = const_cast<HWCBuffer &>(layer->GetLayerBuffer());
AddFenceToMonitor(buffer.fenceId, FENCE_TYPE_ENUM::FENCE_DIRECT_ACQUIRE, layer->GetId());
HWCIapiAdapter::GetInstance().FreshDirect(m_frameBufferFd, buffer);
AddFenceToMonitor(buffer.releaseFenceFd, FENCE_TYPE_ENUM::FENCE_DIRECT_RELEASE, layer->GetId());
return HWC_SUCCESS;
}
int HWCDisplay::HwcFbFresh()
{
HWC_CHK_RETURN((m_frameBufferFd < 0), HWC_FAILURE, ALOGE("HwcFbFresh fb fd is invalid: %d", m_frameBufferFd));
const private_handle_t *fbHandle = static_cast<const private_handle_t *>(m_clientTarget.buffer);
HWC_CHK_RETURN((fbHandle == nullptr), HWC_FAILURE, ALOGE("HwcFbFreshfbHandle is null"));
// capture layer buffer or wait for debug
CaptureLayerIfNecessary();
WaitIfNecessary();
if (m_composeType != (int)HWC_COMPOSE::HWC_GPU_COMPOSE) {
// if gfx2d compose, mark it. so gpu will know the compose switch and do global switch
int val = 1;
gralloc_buffer_attr_write(const_cast<private_handle_t *>(fbHandle), GRALLOC_BUFFER_ATTR_COMPOSE, &val);
}
// don't fresh fb when no swap buffer
if (m_lastIonPhyAddr == fbHandle->addr) {
if (m_clientTarget.fenceId != HWC_INVALID_FENCE_ID) {
ALOGE("repeat frame should not have valid acquire fence");
}
ALOGI("repeat frame ion_phy=0x%x composeType=%d", fbHandle->addr, m_composeType);
m_lastRetireFenceId = HWC_INVALID_FENCE_ID;
return HWC_SUCCESS;
}
if (m_composeType == (int)HWC_COMPOSE::HWC_GFX2D_COMPOSE) {
// close frame buffer acquire fence for gfx compose
if (m_clientTarget.fenceId != HWC_INVALID_FENCE_ID) {
close(m_clientTarget.fenceId);
}
// switch frame buffer release fence of gfx compose to be acquire fence of fresh fb
if (m_hwcAsyncCompose) {
m_clientTarget.fenceId = m_clientTarget.releaseFenceFd;
} else {
m_clientTarget.fenceId = HWC_INVALID_FENCE_ID;
}
}
m_clientTarget.releaseFenceFd = HWC_INVALID_FENCE_ID;
AddFenceToMonitor(m_clientTarget.fenceId, FENCE_TYPE_ENUM::FENCE_FB_ACQUIRE, 0);
HWCIapiAdapter::GetInstance().FreshFb(m_frameBufferFd, m_clientTarget, m_composeType, m_hwcCompress,
m_displayType != DisplayType::DISPLAY_PRIMARY);
m_lastIonPhyAddr = fbHandle->addr;
m_lastRetireFenceId = m_retireFenceId;
if (m_clientTarget.releaseFenceFd != HWC_INVALID_FENCE_ID) {
m_retireFenceId = m_clientTarget.releaseFenceFd;
#ifdef EXT_GFX2D_SUPPORT
int s32HWCReleaseFenceFd = dup(m_clientTarget.releaseFenceFd);
AddFbReleaseFence(fbHandle->addr, s32HWCReleaseFenceFd);
#endif
m_clientTarget.releaseFenceFd = HWC_INVALID_FENCE_ID;
} else {
m_retireFenceId = HWC_INVALID_FENCE_ID;
#ifdef EXT_GFX2D_SUPPORT
AddFbReleaseFence(fbHandle->addr, HWC_INVALID_FENCE_ID);
#endif
}
return HWC_SUCCESS;
}
uint32_t HWCDisplay::ReadFbPixel(private_handle_t &fbHandle, const int x, const int y)
{
const int fbNum = 3;
const int pixelBytes = 4;
int stride = fbHandle.bytes_stride;
if (m_fbAddr == nullptr) {
m_fbAddr = mmap(nullptr, fbHandle.size * fbNum, PROT_READ, MAP_SHARED, m_frameBufferFd, 0);
}
HWC_CHK_RETURN((m_fbAddr == nullptr), HWC_SUCCESS, ALOGD("ReadFbPixel m_fbAddr null"));
unsigned char *addr = static_cast<unsigned char *>(m_fbAddr) + fbHandle.offset;
int pixelPostion = y * stride + x * pixelBytes;
if ((pixelPostion + pixelBytes) > fbHandle.size) {
ALOGE("the pixel is outside");
return 0;
}
auto data = reinterpret_cast<uint32_t *>(addr + pixelPostion);
return *data;
}
void HWCDisplay::SaveFbRgbFile(const std::string &filename, const private_handle_t &fbHandle)
{
const int fbNum = 3;
if (m_fbAddr == nullptr) {
m_fbAddr = mmap(nullptr, fbHandle.size * fbNum, PROT_READ, MAP_SHARED, m_frameBufferFd, 0);
}
HWC_CHK_RETURN_NOT_VALUE((m_fbAddr == nullptr), ALOGE("SaveFbRgbFile m_fbAddr null"));
unsigned char *addr = static_cast<unsigned char *>(m_fbAddr) + fbHandle.offset;
int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
if (fd < 0) {
ALOGE("saveRgbFile fopen %s failed errno:%d ", filename.c_str(), errno);
return;
}
int hasWriten = write(fd, addr, fbHandle.size);
ALOGI("saveRgbFile %s has write %d size %d", filename.c_str(), hasWriten, fbHandle.size);
close(fd);
}
void HWCDisplay::CheckFbPresentPixel()
{
static uint32_t testCount = 0;
HWC_CHK_RETURN_NOT_VALUE((m_frameBufferFd < 0), ALOGE("chk pixel fb fd is invalid: %d", m_frameBufferFd));
struct private_handle_t *fbHandle = (struct private_handle_t *)(m_clientTarget.buffer);
HWC_CHK_RETURN_NOT_VALUE((fbHandle == nullptr), ALOGE("HwcFbFreshfbHandle is null"));
const int x = property_get_int32("vendor.gfx.hwc.debug.fbbuffer.x", -1);
const int y = property_get_int32("vendor.gfx.hwc.debug.fbbuffer.y", -1);
if ((x < 0) || (y < 0) || (x >= static_cast<int>(m_displayFbDevice->width)) ||
(y >= static_cast<int>(m_displayFbDevice->height))) {
return;
}
SyncWait(m_clientTarget.fenceId, HWC_FENCE_TIMEOUT);
char value[PROPERTY_VALUE_MAX] = {0};
property_get("vendor.gfx.hwc.debug.fbbuffer.color", value, nullptr);
uint32_t color = strtoul(value, nullptr, HWC_HEX);
uint32_t fbColor = ReadFbPixel(*fbHandle, x, y);
ALOGI("testCount %d x: %d y: %d wantColor %x, fbColor %x offset: %lu fbHandle->bytes_stride %d",
testCount, x, y, color, fbColor, fbHandle->offset, fbHandle->bytes_stride);
// check the pixel
if (fbColor != color) {
ALOGE("has detect the unnormal pixel testCount %d", testCount);
testCount++;
// save fb buffer
std::stringstream os;
os << "/data/hwc_fbbuffer_" << testCount << ".bmp";
const std::string fname = os.str();
SaveFbRgbFile(fname, *fbHandle);
ALOGE("has detect the abnormal pixel sleep 3");
sleep(HWC_PIXEL_CHECK_TIMEOUT);
}
}
void HWCDisplay::WaitFenceBeforeSyncCompose() const
{
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
if ((*layer)->GetLayerBuffer().fenceId >= 0) {
int64_t syncWaitStart = systemTime(SYSTEM_TIME_MONOTONIC) / TIME_CONVERSION_LEVEL;
int err = SyncWait((*layer)->GetLayerBuffer().fenceId, HWC_FENCE_TIMEOUT);
if (err < 0 && errno == ETIME) {
ALOGE("sync_wait layer(%" PRIu64 ") for over 3s", (*layer)->GetId());
}
if (m_codeStateFlag) {
ALOGD("sync_wait layer(%" PRIu64 ") fence for %" PRId64 "us", (*layer)->GetId(),
systemTime(SYSTEM_TIME_MONOTONIC) / TIME_CONVERSION_LEVEL - syncWaitStart);
}
}
}
if (m_clientTarget.fenceId != HWC_INVALID_FENCE_ID) {
int64_t syncWaitStart = systemTime(SYSTEM_TIME_MONOTONIC) / TIME_CONVERSION_LEVEL;
int err = SyncWait(m_clientTarget.fenceId, HWC_FENCE_TIMEOUT);
if (err < 0 && errno == ETIME) {
ALOGE("sync_wait fb fence for over 3 seconds");
}
if (m_codeStateFlag) {
ALOGD("sync_wait fb fence for %" PRId64 "us",
systemTime(SYSTEM_TIME_MONOTONIC) / TIME_CONVERSION_LEVEL - syncWaitStart);
}
} else {
// framebuffer handle
const private_handle_t *handle = static_cast<const private_handle_t *>(m_clientTarget.buffer);
if (handle != nullptr) {
ALOGE("invalide fence fb(-1) (addr=0x%x) , do not wait", handle->addr);
}
}
}
int HWCDisplay::HwcGfx2DComposer()
{
ATRACE_CALL();
const unsigned int layerNum = m_layers.size();
HWC_CHK_RETURN((m_displayId == HWC_DISPLAY_VIRTUAL), HWC_FAILURE, ALOGD("virtual screen not hwc"));
HWC_CHK_RETURN((m_frameBufferFd < 0), HWC_FAILURE, ALOGE("HwcGfx2DComposer fb fd is invalid:%d", m_frameBufferFd));
HWC_CHK_RETURN((layerNum <= 0), HWC_FAILURE, ALOGE("HWC hwcGfx2DComposer have no layer to compose"));
int layerReleaseFences[layerNum];
errno_t eok = memset_s(layerReleaseFences, layerNum * sizeof(int), 0, layerNum * sizeof(int));
HWC_CHK_RETURN((eok != EOK), HWC_FAILURE, ALOGE("layerReleaseFences memset_s failed"));
HWC_CHK_RETURN(m_powerMode != HWC2::PowerMode::On, HWC_FAILURE,
ALOGD("powermode is not on"));
int fbRealeaseFence;
unsigned int i = 0;
if (m_hwcCompress) {
char displayMode[PROPERTY_VALUE_MAX] = { 0 };
string displayMode2D = "2D";
property_get("vendor.display.format.mode", displayMode, "2D");
if (strncmp(displayMode, displayMode2D.c_str(), displayMode2D.length()) != 0) {
m_hwcCompress = false; // the frame compress
}
}
// Step 1. prepare to allocate memory which is used to record the gfx2d compose info
int ret = HWCIapiAdapter::GetInstance().PrepareGfx2dBasicArg(layerNum);
if (ret != HWC_SUCCESS) {
HWCIapiAdapter::GetInstance().PostGfx2dCompose();
return ret;
}
// Step 2. prepare input(layer info) for gfx2d compose
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
if ((*layer)->GetDeviceSelected() == HWC2::Composition::Client) {
ALOGE("Error: gfx2dCompose have gpu layer ");
HWCIapiAdapter::GetInstance().PostGfx2dCompose();
return ret;
}
ret = HWCIapiAdapter::GetInstance().PrepareGfx2dLayerArg(*(*layer), i, m_displayFbDevice->width,
m_displayFbDevice->height, m_hwcAsyncCompose);
if (ret != HWC_SUCCESS) {
HWCIapiAdapter::GetInstance().PostGfx2dCompose();
return ret;
}
i++;
}
// Step 3. prepare ouput(framebuffer info) for gfx2d compose
ret = HWCIapiAdapter::GetInstance().PrepareGfx2dDestArg(m_frameBufferFd, m_clientTarget, m_hwcCompress);
if (ret != HWC_SUCCESS) {
HWCIapiAdapter::GetInstance().PostGfx2dCompose();
return ret;
}
// Step 4. wait fb and layer acquire fence for async compose, and close them later to avoid fd leak
if (!m_hwcAsyncCompose) {
WaitFenceBeforeSyncCompose();
}
// Step 5. process gfx2d compose
ret = HWCIapiAdapter::GetInstance().Gfx2dCompose(layerReleaseFences, layerNum, fbRealeaseFence, m_hwcAsyncCompose);
if (ret != HWC_SUCCESS) {
HWCIapiAdapter::GetInstance().PostGfx2dCompose();
return ret;
}
i = 0;
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
if ((*layer)->GetDeviceSelected() != HWC2::Composition::Device) {
if (layerReleaseFences[i] != HWC_INVALID_FENCE_ID) {
close(layerReleaseFences[i]);
}
layerReleaseFences[i] = HWC_INVALID_FENCE_ID;
}
(*layer)->SetLayerBufferReleaseFence(layerReleaseFences[i]);
AddFenceToMonitor((*layer)->GetLayerBuffer().fenceId, FENCE_TYPE_ENUM::FENCE_FB_ACQUIRE, (*layer)->GetId());
AddFenceToMonitor(layerReleaseFences[i], FENCE_TYPE_ENUM::FENCE_FB_ACQUIRE, (*layer)->GetId());
i++;
}
m_clientTarget.releaseFenceFd = fbRealeaseFence;
// Step 6. free the memory allocated in the step 1.
HWCIapiAdapter::GetInstance().PostGfx2dCompose();
return ret;
}
HWCLayer* HWCDisplay::GetHWCLayer(hwc2_layer_t layer)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
const auto mapLayer = m_layerMap.find(layer);
if (mapLayer == m_layerMap.end()) {
ALOGE("[%" PRIu64 "] getLayer(%" PRIu64 ") failed: no such layer", m_displayId, layer);
return nullptr;
} else {
return mapLayer->second;
}
}
HWC2::Error HWCDisplay::AcceptDisplayChanges(void)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
HWC_CHK_RETURN((!m_validated && !m_layers.empty()), HWC2::Error::NotValidated,
ALOGD("hwc accept display changes validate=%d", m_validated));
HWC_CHK_RETURN(m_layerChanges.empty(), HWC2::Error::None);
for (auto change = m_layerChanges.cbegin(); change != m_layerChanges.cend(); ++change) {
auto hwcLayer = m_layerMap[(*change).first];
auto composition = (*change).second;
if (hwcLayer == nullptr) {
ALOGI("Null layer in HWCDisplay::AcceptDisplayChanges.");
} else {
hwcLayer->UpdateClientCompositionType(composition);
}
}
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::CreateLayer(hwc2_layer_t *outLayerId)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
HWC_CHK_RETURN((outLayerId == nullptr), HWC2::Error::BadParameter,
ALOGE("HWC:createLayer get null input"));
const auto layer = *m_layers.emplace(new HWCLayer(m_displayId));
m_layerMap.emplace(std::make_pair(layer->GetId(), layer));
*outLayerId = layer->GetId();
ALOGV("[%" PRIu64 "] created layer %" PRIu64, m_displayId, *outLayerId);
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::DestroyLayer(hwc2_layer_t layerId)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
const auto mapLayer = m_layerMap.find(layerId);
HWC_CHK_RETURN((mapLayer == m_layerMap.end()), HWC2::Error::BadLayer,
ALOGE("[%" PRIu64 "] destroyLayer(%" PRIu64 ") failed: no such layer", m_displayId, layerId));
auto layer = mapLayer->second;
m_layerMap.erase(mapLayer);
auto zRange = m_layers.equal_range(layer);
for (auto current = zRange.first; current != zRange.second; ++current) {
if (*current == layer) {
current = m_layers.erase(current);
delete layer;
break;
}
}
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::GetActiveConfig(hwc2_config_t *outConfig)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
HWC_CHK_RETURN((outConfig == nullptr), HWC2::Error::BadParameter,
ALOGE("getActiveConfig outConfig is null"));
*outConfig = m_activeConfig;
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::GetChangedCompositionTypes(uint32_t *outNumElements, hwc2_layer_t *outLayers,
int32_t *outTypes)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
HWC_CHK_RETURN((m_layers.empty()), HWC2::Error::None, ALOGI("GetChangedCompositionTypes with no layer"));
HWC_CHK_RETURN((outNumElements == nullptr), HWC2::Error::BadParameter,
ALOGE("HWC:getChangedCompositionTypes get null input"));
HWC_CHK_RETURN(!m_validated, HWC2::Error::NotValidated, ALOGW("Display is not validated"));
*outNumElements = uint32_t(m_layerChanges.size());
HWC_CHK_RETURN(m_layerChanges.empty(), HWC2::Error::None);
if (outLayers != nullptr && outTypes != nullptr) {
uint32_t i = 0;
for (auto change = m_layerChanges.cbegin(); change != m_layerChanges.cend(); ++change) {
if (i < *outNumElements) {
outLayers[i] = (*change).first;
outTypes[i] = int32_t((*change).second);
}
i++;
}
}
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::GetClientTargetSupport(uint32_t width, uint32_t height, int32_t format,
int32_t dataSpace)
{
UNUSED(width);
UNUSED(height);
UNUSED(format);
UNUSED(dataSpace);
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::GetColorModes(uint32_t *outNumModes, int32_t *outModes)
{
HWC_CHK_RETURN((outNumModes == nullptr), HWC2::Error::BadParameter, ALOGE("GetColorModes outNumModes is nullptr"));
*outNumModes = 0;
if (outModes != nullptr) {
*outModes = 0;
}
return HWC2::Error::Unsupported;
}
HWC2::Error HWCDisplay::GetDisplayAttribute(hwc2_config_t config, HWC2::Attribute attribute, int32_t *outValue)
{
UNUSED(config);
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
HWC_CHK_RETURN((outValue == nullptr), HWC2::Error::BadParameter,
ALOGE("GetDisplayAttribute with null input"));
const auto configInfo = m_displayConfigs.find(config);
HWC_CHK_RETURN((configInfo == m_displayConfigs.end()), HWC2::Error::BadParameter,
ALOGE("GetDisplayAttribute failed for config:%d", config));
DisplayConfigInfo displayConfig = configInfo->second;
const uint32_t yDpiUnit = 1000;
switch (attribute) {
case HWC2::Attribute::VsyncPeriod:
*outValue = int32_t(1e9 / displayConfig.fps);
ALOGI("GetDisplayAttribute *outValue:%d", *outValue);
break;
case HWC2::Attribute::Width:
*outValue = displayConfig.width;
ALOGI("GetDisplayAttribute Width *outValue:%d", *outValue);
break;
case HWC2::Attribute::Height:
*outValue = displayConfig.height;
ALOGI("GetDisplayAttribute Height *outValue:%d", *outValue);
break;
case HWC2::Attribute::DpiX:
*outValue = int32_t(displayConfig.xDpi * yDpiUnit);
ALOGI("GetDisplayAttribute DpiX *outValue:%d", *outValue);
break;
case HWC2::Attribute::DpiY:
*outValue = int32_t(displayConfig.yDpi * yDpiUnit);
ALOGI("GetDisplayAttribute DpiY *outValue:%d", *outValue);
break;
default:
ALOGE("Unknown display attribute %u", attribute);
break;
}
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::GetDisplayConfigs(uint32_t *outNumConfigs, hwc2_config_t *outConfigs)
{
HWC_CHK_RETURN((outNumConfigs == nullptr), HWC2::Error::BadParameter,
ALOGE("HWC:getDisplayConfigs get null input"));
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
if (outConfigs == nullptr) {
*outNumConfigs = m_displayConfigs.size();
ALOGI("GetDisplayConfigs outNumConfigs:%d", *outNumConfigs);
} else {
uint32_t i = 0;
for (auto config = m_displayConfigs.cbegin(); config != m_displayConfigs.cend(); ++config) {
if (i < *outNumConfigs) {
outConfigs[i] = (*config).first;
ALOGI("GetDisplayConfigs config:%d", outConfigs[i]);
}
i++;
}
}
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::GetDisplayName(uint32_t *outSize, char *outName)
{
HWC_CHK_RETURN((outSize == nullptr), HWC2::Error::BadParameter, ALOGE("GetDisplayName outSize is null"));
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
const uint32_t oSize = 32;
if (outName == nullptr) {
*outSize = oSize;
} else {
std::string name;
switch (m_displayId) {
case HWC_DISPLAY_PRIMARY:
name = "Primary Display";
break;
case HWC_DISPLAY_EXTERNAL:
name = "External Display";
break;
case HWC_DISPLAY_VIRTUAL:
name = "Virtual Display";
break;
default:
name = "Unknown";
break;
}
errno_t ret = strncpy_s(outName, oSize + 1, name.c_str(), name.size());
HWC_CHK_RETURN((ret != EOK), HWC2::Error::NotValidated,
ALOGE("ERR :%s LINE %d | GetDisplayName strncpy_s ret(%x)", __FUNCTION__, __LINE__, ret));
*outSize = uint32_t(name.size());
}
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::GetDisplayRequests(int32_t *outDisplayRequests, uint32_t *outNumElements,
hwc2_layer_t *outLayers, int32_t *outLayerRequests)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
HWC_CHK_RETURN(m_layers.empty(), HWC2::Error::None);
HWC_CHK_RETURN((outDisplayRequests == nullptr || outNumElements == nullptr), HWC2::Error::BadParameter,
ALOGE("HWC:getDisplayRequests get null input"));
HWC_CHK_RETURN(!m_validated, HWC2::Error::NotValidated, ALOGW("Display is not validated"));
*outDisplayRequests = int32_t(m_displayRequests);
*outNumElements = uint32_t(m_layerRequests.size());
HWC_CHK_RETURN(m_layerRequests.empty(), HWC2::Error::None);
if (outLayers != nullptr && outLayerRequests != nullptr) {
uint32_t i = 0;
for (auto request = m_layerRequests.cbegin(); request != m_layerRequests.cend(); ++request) {
if (i < *outNumElements) {
outLayers[i] = (*request).first;
outLayerRequests[i] = int32_t((*request).second);
}
i++;
}
}
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::GetDisplayType(int32_t *outType)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
HWC_CHK_RETURN((outType == nullptr), HWC2::Error::BadParameter, ALOGE("GetDisplayType outType is null"));
if (m_displayId == HWC_DISPLAY_VIRTUAL) {
*outType = HWC2_DISPLAY_TYPE_VIRTUAL;
} else {
*outType = HWC2_DISPLAY_TYPE_PHYSICAL;
}
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::GetReleaseFences(uint32_t *outNumElements, hwc2_layer_t *outLayers, int32_t *outFences)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
HWC_CHK_RETURN(m_layers.empty(), HWC2::Error::None);
HWC_CHK_RETURN((outNumElements == nullptr), HWC2::Error::BadParameter,
ALOGE("HWC:GetReleaseFences get null input"));
*outNumElements = uint32_t(m_layers.size());
if (outLayers != nullptr && outFences != nullptr) {
uint32_t i = 0;
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
if (i < *outNumElements) {
outLayers[i] = (*layer)->GetId();
outFences[i] = (*layer)->PopReleaseFence();
}
i++;
}
}
return HWC2::Error::None;
}
int HWCDisplay::GetFrameBufferFd() const
{
return m_frameBufferFd;
}
HWC2::Error HWCDisplay::SetLayerZOrder(hwc2_layer_t layerId, uint32_t z)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
const auto mapLayer = m_layerMap.find(layerId);
HWC_CHK_RETURN((mapLayer == m_layerMap.end()), HWC2::Error::BadLayer,
ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer", m_displayId));
auto layer = mapLayer->second;
auto zRange = m_layers.equal_range(layer);
bool layerOnDisplay = false;
for (auto current = zRange.first; current != zRange.second; ++current) {
if (*current == layer) {
if ((*current)->GetLayerZOrder() == z) {
// Don't change anything if the Z hasn't changed
return HWC2::Error::None;
}
current = m_layers.erase(current);
layerOnDisplay = true;
break;
}
}
HWC_CHK_RETURN((!layerOnDisplay), HWC2::Error::BadLayer,
ALOGE("[%" PRIu64 "] updateLayerZ failed to find layer on display", m_displayId));
layer->SetLayerZOrder(z);
m_layers.emplace(layer);
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::SetOutputBuffer(buffer_handle_t buf, int32_t releaseFence)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
ALOGV("[%" PRIu64 "] setOutputBuffer(%p, %d)", m_displayId, buf, releaseFence);
m_outputBuffer.buffer = buf;
m_outputBuffer.fenceId = releaseFence;
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::SetPowerMode(HWC2::PowerMode mode)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
HWC_CHK_RETURN((!IsValidPowerMode(mode)), HWC2::Error::BadParameter);
HWC_CHK_RETURN((mode == m_powerMode && mode != HWC2::PowerMode::On), HWC2::Error::None);
// Required only for battery products
string tabletProduct = "tablet";
string xrProduct = "xr";
char value[PROPERTY_VALUE_MAX] = {0};
property_get("ro.build.product", value, "");
if ((strncmp(value, tabletProduct.c_str(), tabletProduct.length()) == 0) ||
(strncmp(value, xrProduct.c_str(), xrProduct.length()) == 0)) {
ALOGI("need set disp power state, power mode is %d ", mode);
HWCIapiAdapter::GetInstance().SetDispPowerState(m_displayId, mode);
}
m_powerMode = mode;
// when change attach/detach, SF will set powerMode to stop/start draw on external display,
// set fb alpha to 0 prevent the last frame cover up external attach or
// set fb alpha recover normal.
if (m_displayType == DisplayType::DISPLAY_EXTERNAL) {
RefreshFb(m_powerMode);
}
return HWC2::Error::None;
}
void HWCDisplay::RefreshFb(const HWC2::PowerMode mode)
{
if (mode == HWC2::PowerMode::Off) {
m_couldDisplay = false;
} else if (mode == HWC2::PowerMode::On) {
uapi_disp_state dispStatus = {};
int ret = HWCIapiAdapter::GetInstance().GetExternalDisplayStatus(&dispStatus);
if (ret != HWC_SUCCESS) {
ALOGE("GetExternalDisplayStatus fail");
return;
}
ALOGI("external display is slaved: %s", dispStatus.is_slave ? "true" : "false");
// if external display is slave with primary display, we close external display.
if (dispStatus.is_slave) {
m_couldDisplay = false;
} else {
m_couldDisplay = true;
}
} else {
ALOGI("Refresh fb show status ignore! unknow powermode, %d", static_cast<int>(mode));
return;
}
ALOGI("external display is showable: %s", m_couldDisplay ? "true" : "false");
if (ioctl(m_frameBufferFd, GFBGIOPUT_SHOW, &m_couldDisplay) != 0) {
ALOGE("GFBGIOPUT_SHOW Error!");
}
}
HWC2::Error HWCDisplay::SetVsyncEnabled(HWC2::Vsync enabled)
{
std::unique_lock<std::mutex> lock(m_vsyncMutex);
m_vsyncEnabled = enabled;
if (m_vsyncEnabled == HWC2::Vsync::Enable) {
m_vsyncCondition.notify_one();
}
return HWC2::Error::None;
}
bool HWCDisplay::Vsync(int64_t timestamp, unsigned int freshRate)
{
// m_freshRate is activeMode's fresh_rate, freshRate is vsync freshRate
int gap = 1;
if (m_freshRate <= freshRate) {
gap = freshRate / m_freshRate;
} else {
ALOGE("vsync warnning! m_freshRate is %d, freshRate is %d", m_freshRate, freshRate);
}
std::unique_lock<std::mutex> lock(m_vsyncMutex);
if (m_vsyncEnabled != HWC2::Vsync::Enable) {
m_vsyncCount = 0;
m_vsyncCondition.wait(lock);
} else {
if (m_hwcCallbacks == nullptr) {
return false;
}
if (((m_vsyncCount++) % gap) == 0) {
m_hwcCallbacks->Vsync(m_displayId, timestamp);
}
return true;
}
return false;
}
inline void HWCDisplay::AddFenceToMonitor(int fd, FENCE_TYPE type, hwc2_layer_t layerId)
{
if (m_fenceMonitorDebug || CC_UNLIKELY((atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS) != 0))) {
if (m_fenceMonitorMng == nullptr) {
m_fenceMonitorMng = std::make_unique<FenceMonitorMng>();
}
m_fenceMonitorMng->AddFence(fd, type, layerId, m_seq);
}
}
void HWCDisplay::UpdateRequestAndComposeType()
{
m_layerRequests.clear();
m_displayRequests = static_cast<HWC2::DisplayRequest>(0);
if (m_layers.empty()) {
return;
}
HWCLayer* firstLayer = *m_layers.begin();
switch (m_composeType) {
case (int)HWC_COMPOSE::HWC_GPU_COMPOSE:
// any layer not supported by hwc, all layers will be added to GPU
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
PrivHandle handle = static_cast<PrivHandle>((*layer)->GetLayerBuffer().buffer);
if (((*layer)->GetClientRequested() == HWC2::Composition::Sideband) || ((handle != nullptr) &&
static_cast<bool>((handle->consumer_usage | handle->producer_usage) &
static_cast<unsigned long long>(GRALLOC_USAGE_PRI_VDP)))) {
(*layer)->UpdateDeviceCompositionType(HWC2::Composition::Sideband);
m_layerRequests[(*layer)->GetId()] = HWC2::LayerRequest::ClearClientTarget;
} else {
(*layer)->UpdateDeviceCompositionType(HWC2::Composition::Client);
}
}
break;
case (int)HWC_COMPOSE::HWC_GFX2D_COMPOSE:
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
PrivHandle handle = static_cast<PrivHandle>((*layer)->GetLayerBuffer().buffer);
if (((*layer)->GetClientRequested() == HWC2::Composition::Sideband) || ((handle != nullptr) &&
static_cast<bool>((handle->consumer_usage | handle->producer_usage) &
static_cast<unsigned long long>(GRALLOC_USAGE_PRI_VDP)))) {
(*layer)->UpdateDeviceCompositionType(HWC2::Composition::Sideband);
} else {
(*layer)->UpdateDeviceCompositionType(HWC2::Composition::Device);
// need to sed layer requests, otherwise, surfaceflinger can't get display request.
// sideband should avoid digging holes twice both by GPU and gfx2d
m_layerRequests[(*layer)->GetId()] = HWC2::LayerRequest::ClearClientTarget;
}
}
if (!static_cast<bool>(property_get_bool(m_lockFb.c_str(), false))) {
m_displayRequests = HWC2::DisplayRequest::FlipClientTarget;
}
break;
case (int)HWC_COMPOSE::HWC_DIRECT_PRESENT:
// Select device compostion to avoid GPU Composition in surfaceflinger
// and don't set FlipClientTarget to ensure that frame buffer was not udpated
firstLayer->UpdateDeviceCompositionType(HWC2::Composition::Device);
break;
default:
ALOGE("wrong compose type:%d", m_composeType);
break;
}
}
HWC2::Error HWCDisplay::ValidateDisplay(uint32_t *outNumTypes, uint32_t *outNumRequests)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
// if screen off , not composer and send fb
HWC_CHK_RETURN((m_powerMode != HWC2::PowerMode::On), HWC2::Error::None, ALOGD("HWC power mode not On"));
HWC_CHK_RETURN((outNumTypes == nullptr || outNumRequests == nullptr), HWC2::Error::BadParameter,
ALOGE("HWC:validateDisplay get null input"));
m_codeStateFlag = property_get_bool("vendor.gfx.hwc.stat", false);
m_hwcAsyncCompose = property_get_bool("persist.vendor.gfx.gfx2d.async", true);
m_layerChanges.clear();
m_composeType = GetComposeType();
UpdateRequestAndComposeType();
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
if ((*layer)->GetDeviceSelected() != (*layer)->GetClientRequested()) {
m_layerChanges[(*layer)->GetId()] = (*layer)->GetDeviceSelected();
}
}
*outNumTypes = uint32_t(m_layerChanges.size());
*outNumRequests = uint32_t(m_layerRequests.size());
m_validated = true;
HWC_CHK_RETURN((*outNumTypes > 0), HWC2::Error::HasChanges);
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::PresentDisplay(int32_t *outRetireFence)
{
ATRACE_CALL();
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
// if screen off , not composer and send fb.
HWC_CHK_RETURN((m_powerMode != HWC2::PowerMode::On), HWC2::Error::None, ALOGD("powermode is off"));
HWC_CHK_RETURN((!m_couldDisplay), HWC2::Error::None, ALOGD("couldn't display, because display is close"));
HWC_CHK_RETURN((outRetireFence == nullptr), HWC2::Error::BadParameter,
ALOGE("presentDisplay get null input"));
HWC_CHK_RETURN((m_displayId == HWC_DISPLAY_VIRTUAL), HWC2::Error::None);
if (static_cast<bool>(property_get_bool(m_lockFb.c_str(), false))) {
WaitFenceBeforeSyncCompose();
return HWC2::Error::None;
}
// whether gfx2d compress graphic data
if (m_hwcCompose && (m_composeType == (int)HWC_COMPOSE::HWC_GFX2D_COMPOSE)) {
(void)HwcGfx2DComposer();
}
HwcMediaFresh();
if (m_composeType != (int)HWC_COMPOSE::HWC_DIRECT_PRESENT) {
HwcFbFresh();
if (m_fbPixelDebug) {
CheckFbPresentPixel();
}
} else {
HwcDirectFresh();
}
AddFenceToMonitor(m_retireFenceId, FENCE_TYPE_ENUM::FENCE_FB_ACQUIRE, 0);
m_seq++;
bool enableDebug = property_get_bool("vendor.gfx.hwc.debug", false);
// Close layer acquire fence to avoid fd leak and push release fence
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
if (enableDebug) {
(*layer)->Dump();
}
(*layer)->CloseAcquireFence();
(*layer)->PushReleaseFence();
}
// close frame buffer acquire fence to avoid fd leak
if (m_clientTarget.fenceId != HWC_INVALID_FENCE_ID) {
// here only for gpu compose ,we don't need to wait fence only close it or else fd will be leaked
close(m_clientTarget.fenceId);
m_clientTarget.fenceId = HWC_INVALID_FENCE_ID;
}
*outRetireFence = m_lastRetireFenceId;
if (enableDebug) {
ALOGI("PresentDisplay return with fence:%d", m_lastRetireFenceId);
}
return HWC2::Error::None;
}
void HWCDisplay::UpdateVirtualScreenByConfig()
{
#ifdef EXT_XR
// for XR special scene, virtual screen only should be 3840 x 1080 or 2560 x 720,
// which is not completely equal to curren resolution, such as 1920 x 1080, 1280 x 720.
if ((m_displayFbDevice->width == HWC_XR_3D_WIDTH) && (m_displayFbDevice->height == HWC_XR_3D_HEIGHT)) {
if (m_activeDisplayConfig.height == HWC_XR_3D_HEIGHT) {
HWCIapiAdapter::GetInstance().UpdateVirtualScreen(m_displayId, HWC_XR_3D_WIDTH, HWC_XR_3D_HEIGHT);
} else if (m_activeDisplayConfig.height == HWC_XR_3D_OLD_HEIGHT) {
HWCIapiAdapter::GetInstance().UpdateVirtualScreen(m_displayId, HWC_XR_3D_OLD_WIDTH, HWC_XR_3D_OLD_HEIGHT);
}
return;
}
#endif
HWCIapiAdapter::GetInstance().UpdateVirtualScreen(m_displayId, m_activeDisplayConfig.width,
m_activeDisplayConfig.height);
}
HWC2::Error HWCDisplay::SetActiveConfig(hwc2_config_t config)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
m_activeConfig = config;
ALOGI("SetActiveConfig %d", m_activeConfig);
m_activeDisplayConfig = m_displayConfigs[config];
UpdateVirtualScreenByConfig();
m_freshRate = m_activeDisplayConfig.fps;
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::SetClientTarget(buffer_handle_t target, int32_t acquireFence, int32_t dataSpace,
hwc_region_t damage)
{
// dataspace and damage can't be used now, so ignore them
UNUSED(dataSpace);
UNUSED(damage);
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
ALOGV("[%" PRIu64 "] setClientTarget(%p, %d)", m_displayId, target, acquireFence);
m_clientTarget.buffer = target;
#ifdef EXT_GFX2D_SUPPORT
int fbReleaseFd = HWC_INVALID_FENCE_ID;
const private_handle_t *fbHandle = static_cast<const private_handle_t *>(target);
if (fbHandle != nullptr) {
QueryFbReleaseFence(fbHandle->addr, fbReleaseFd);
}
if (m_composeType == (int)HWC_COMPOSE::HWC_GFX2D_COMPOSE) {
// for gfx2d compose, init the value of clientTrage.fenceId using stored value
if (acquireFence != HWC_INVALID_FENCE_ID) {
// to avoid fd leak
close(acquireFence);
acquireFence = HWC_INVALID_FENCE_ID;
}
m_clientTarget.fenceId = fbReleaseFd;
} else {
if (fbReleaseFd != HWC_INVALID_FENCE_ID) {
// to avoid fd leak
close(fbReleaseFd);
fbReleaseFd = HWC_INVALID_FENCE_ID;
}
m_clientTarget.fenceId = acquireFence;
}
#else
m_clientTarget.fenceId = acquireFence;
#endif
m_clientTarget.releaseFenceFd = HWC_INVALID_FENCE_ID;
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::SetColorMode(int32_t mode)
{
UNUSED(mode);
return HWC2::Error::None;
}
HWC2::Error HWCDisplay::SetCursorPosition(hwc2_layer_t layer, int x, int y)
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
ALOGV("[%" PRIu64 "] setCursorPosition(%d, %d)", layer, x, y);
return HWC2::Error::Unsupported;
}
HWC2::Error HWCDisplay::GetHdrCapabilities(uint32_t *outNumTypes, int32_t *outTypes, float *outMaxLuminance,
float *outMaxAverageLuminance, float *outMinLuminance)
{
UNUSED(outTypes);
HWC_CHK_RETURN((outNumTypes == nullptr || outMaxLuminance == nullptr || outMaxAverageLuminance == nullptr ||
outMinLuminance == nullptr), HWC2::Error::BadParameter, ALOGE("getHdrCapabilities get null input"));
if (property_get_bool("vendor.gfx.hwc.debug.hdr", false)) {
if (outTypes == nullptr) {
*outNumTypes = MAX_HDR_TYPES;
return HWC2::Error::None;
}
outTypes[HDR_ARRAY_INDEX_0] = HAL_HDR_DOLBY_VISION;
outTypes[HDR_ARRAY_INDEX_1] = HAL_HDR_HDR10;
outTypes[HDR_ARRAY_INDEX_2] = HAL_HDR_HLG;
*outMaxLuminance = 0;
*outMaxAverageLuminance = 0;
*outMinLuminance = 0;
return HWC2::Error::None;
}
#ifdef EXT_HDMI_SUPPORT
HdrCap cap;
errno_t eok = memset_s(&cap, sizeof(HdrCap), 0, sizeof(HdrCap));
HWC_CHK_RETURN((eok != EOK), HWC2::Error::NotValidated, ALOGE("HdrCap memset_s failed"));
int ret = HWCIapiAdapter::GetInstance().GetHdrInfo(m_displayId, cap);
HWC_CHK_RETURN((ret == HWC_FAILURE), HWC2::Error::NotValidated);
if (outTypes == nullptr) {
*outNumTypes = cap.num;
return HWC2::Error::None;
}
unsigned int i = 0;
if (cap.dolby) {
outTypes[i++] = HAL_HDR_DOLBY_VISION;
}
if (cap.hdr10) {
outTypes[i++] = HAL_HDR_HDR10;
}
if (cap.hlg) {
outTypes[i++] = HAL_HDR_HLG;
}
if (i != cap.num) {
ALOGE("GetHdrInfo return illegal values i=%d num=%d", i, cap.num);
}
*outNumTypes = cap.num;
*outMaxLuminance = cap.maxLuminance;
*outMaxAverageLuminance = cap.maxAverageLuminance;
*outMinLuminance = cap.minLuminance;
#endif
return HWC2::Error::None;
}
std::string HWCDisplay::Dump()
{
std::unique_lock<std::recursive_mutex> lock(m_stateMutex);
std::stringstream os;
os << "-----------------------------------------------" << std::endl;
os << "HWC2 Display: " << m_displayId << std::endl;
const private_handle_t *fbHandle = static_cast<const private_handle_t *>(m_clientTarget.buffer);
os << " composeType: " << m_composeType << std::endl;
os << " framebuffer: " << std::hex << fbHandle << std::dec << std::endl;
os << " couldDisplay: " << m_couldDisplay << std::endl;
if (fbHandle != nullptr) {
os << " framebuffer size: " << fbHandle->width << " x " << fbHandle->height << std::endl;
}
os << " m_activeConfig: " << m_activeConfig << std::endl;
os << " m_displayConfigs: " << std::endl;
for (auto config = m_displayConfigs.cbegin(); config != m_displayConfigs.cend(); ++config) {
os << " config id:" << (*config).first << " DisplayConfigInfo:"
<< "{" << (*config).second.width << ", " << (*config).second.height << ", "
<< (*config).second.xDpi << ", " << (*config).second.yDpi << ", "
<< (*config).second.fps << "}" << std::endl;
}
for (auto layer = m_layers.cbegin(); layer != m_layers.cend(); ++layer) {
os << "-------------" << std::endl;
os << "layer_id: " << (*layer)->GetId() << std::endl;
os << "\tz: " << (*layer)->GetLayerZOrder() << std::endl;
const private_handle_t *pHandle = static_cast<const private_handle_t *>((*layer)->GetLayerBuffer().buffer);
if (((pHandle != nullptr) &&
static_cast<bool>((pHandle->consumer_usage | pHandle->producer_usage) &
static_cast<unsigned long long>(GRALLOC_USAGE_PRI_VDP)))) {
os << "\tclient(SF) composition: Overlay" << std::endl;
os << "\tdevice(HWC) composition: Overlay" << std::endl;
} else {
os << "\tclient(SF) composition: " << to_string((*layer)->GetClientRequested()) << std::endl;
os << "\tdevice(HWC) composition: " << to_string((*layer)->GetDeviceSelected()) << std::endl;
}
os << "\tplane_alpha: " << std::to_string((*layer)->GetPlaneAlpha()).c_str() << std::endl;
os << "\tbuffer: " << std::hex << pHandle << std::dec << std::endl;
if (pHandle != nullptr) {
os << "\tformat: " << std::hex << pHandle->format << std::dec << std::endl;
}
os << "\tm_sidebandStream: " << std::hex << (*layer)->GetLayerSidebandStream() << std::dec << std::endl;
os << "\tsourceCrop:[" << (*layer)->GetLayerSourceCrop().left << "," << (*layer)->GetLayerSourceCrop().top <<
"," << (*layer)->GetLayerSourceCrop().right << "," << (*layer)->GetLayerSourceCrop().bottom << "]" <<
std::endl;
os << "\tm_displayFrame:[" << (*layer)->GetDisplayFrame().left << "," << (*layer)->GetDisplayFrame().top <<
"," << (*layer)->GetDisplayFrame().right << "," << (*layer)->GetDisplayFrame().bottom << "]" <<
std::endl;
}
if (m_fenceMonitorDebug && (m_fenceMonitorMng != nullptr)) {
os << "-------------" << std::endl;
os << m_fenceMonitorMng->Dump();
}
return os.str();
}
} // namespace android