/* * Copyright 2021 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 "GuestComposer.h" #include #include #include #include #include #include #include #include #include #include #include #include "Device.h" #include "Display.h" #include "Drm.h" #include "Layer.h" namespace android { namespace { using android::hardware::graphics::common::V1_0::ColorTransform; uint64_t AlignToPower2(uint64_t val, uint8_t align_log) { uint64_t align = 1ULL << align_log; return ((val + (align - 1)) / align) * align; } bool LayerNeedsScaling(const Layer& layer) { hwc_rect_t crop = layer.getSourceCropInt(); hwc_rect_t frame = layer.getDisplayFrame(); int fromW = crop.right - crop.left; int fromH = crop.bottom - crop.top; int toW = frame.right - frame.left; int toH = frame.bottom - frame.top; bool not_rot_scale = fromW != toW || fromH != toH; bool rot_scale = fromW != toH || fromH != toW; bool needs_rot = layer.getTransform() & HAL_TRANSFORM_ROT_90; return needs_rot ? rot_scale : not_rot_scale; } bool LayerNeedsBlending(const Layer& layer) { return layer.getBlendMode() != HWC2::BlendMode::None; } bool LayerNeedsAttenuation(const Layer& layer) { return layer.getBlendMode() == HWC2::BlendMode::Coverage; } struct BufferSpec; typedef int (*ConverterFunction)(const BufferSpec& src, const BufferSpec& dst, bool v_flip); int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool vFlip); int ConvertFromRGB565(const BufferSpec& src, const BufferSpec& dst, bool vFlip); int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool vFlip); ConverterFunction GetConverterForDrmFormat(uint32_t drmFormat) { switch (drmFormat) { case DRM_FORMAT_ABGR8888: case DRM_FORMAT_XBGR8888: return &DoCopy; case DRM_FORMAT_RGB565: return &ConvertFromRGB565; case DRM_FORMAT_YVU420: return &ConvertFromYV12; } DEBUG_LOG("Unsupported drm format: %d(%s), returning null converter", drmFormat, GetDrmFormatString(drmFormat)); return nullptr; } bool IsDrmFormatSupported(uint32_t drmFormat) { return GetConverterForDrmFormat(drmFormat) != nullptr; } // Libyuv's convert functions only allow the combination of any rotation // (multiple of 90 degrees) and a vertical flip, but not horizontal flips. // Surfaceflinger's transformations are expressed in terms of a vertical flip, // a horizontal flip and/or a single 90 degrees clockwise rotation (see // NATIVE_WINDOW_TRANSFORM_HINT documentation on system/window.h for more // insight). The following code allows to turn a horizontal flip into a 180 // degrees rotation and a vertical flip. libyuv::RotationMode GetRotationFromTransform(uint32_t transform) { uint32_t rotation = (transform & HAL_TRANSFORM_ROT_90) ? 1 : 0; // 1 * ROT90 bit rotation += (transform & HAL_TRANSFORM_FLIP_H) ? 2 : 0; // 2 * VFLIP bit return static_cast(90 * rotation); } bool GetVFlipFromTransform(uint32_t transform) { // vertical flip xor horizontal flip return ((transform & HAL_TRANSFORM_FLIP_V) >> 1) ^ (transform & HAL_TRANSFORM_FLIP_H); } struct BufferSpec { uint8_t* buffer; std::optional buffer_ycbcr; int width; int height; int cropX; int cropY; int cropWidth; int cropHeight; uint32_t drmFormat; int strideBytes; int sampleBytes; BufferSpec(uint8_t* buffer, std::optional buffer_ycbcr, int width, int height, int cropX, int cropY, int cropWidth, int cropHeight, uint32_t drmFormat, int strideBytes, int sampleBytes) : buffer(buffer), buffer_ycbcr(buffer_ycbcr), width(width), height(height), cropX(cropX), cropY(cropY), cropWidth(cropWidth), cropHeight(cropHeight), drmFormat(drmFormat), strideBytes(strideBytes), sampleBytes(sampleBytes) {} BufferSpec(uint8_t* buffer, int width, int height, int strideBytes) : BufferSpec(buffer, /*buffer_ycbcr=*/std::nullopt, width, height, /*cropX=*/0, /*cropY=*/0, /*cropWidth=*/width, /*cropHeight=*/height, /*drmFormat=*/DRM_FORMAT_ABGR8888, strideBytes, /*sampleBytes=*/4) {} }; int ConvertFromRGB565(const BufferSpec& src, const BufferSpec& dst, bool vFlip) { ATRACE_CALL(); // Point to the upper left corner of the crop rectangle uint8_t* srcBuffer = src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes; uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; int width = src.cropWidth; int height = src.cropHeight; if (vFlip) { height = -height; } return libyuv::RGB565ToARGB(srcBuffer, src.strideBytes, // dstBuffer, dst.strideBytes, // width, height); } int ConvertFromYV12(const BufferSpec& src, const BufferSpec& dst, bool vFlip) { ATRACE_CALL(); // The following calculation of plane offsets and alignments are based on // swiftshader's Sampler::setTextureLevel() implementation // (Renderer/Sampler.cpp:225) auto& srcBufferYCbCrOpt = src.buffer_ycbcr; if (!srcBufferYCbCrOpt) { ALOGE("%s called on non ycbcr buffer", __FUNCTION__); return -1; } auto& srcBufferYCbCr = *srcBufferYCbCrOpt; // The libyuv::I420ToARGB() function is for tri-planar. if (srcBufferYCbCr.chroma_step != 1) { ALOGE("%s called with bad chroma step", __FUNCTION__); return -1; } uint8_t* srcY = reinterpret_cast(srcBufferYCbCr.y); int strideY = srcBufferYCbCr.ystride; uint8_t* srcU = reinterpret_cast(srcBufferYCbCr.cb); int strideU = srcBufferYCbCr.cstride; uint8_t* srcV = reinterpret_cast(srcBufferYCbCr.cr); int strideV = srcBufferYCbCr.cstride; // Adjust for crop srcY += src.cropY * strideY + src.cropX; srcV += (src.cropY / 2) * strideV + (src.cropX / 2); srcU += (src.cropY / 2) * strideU + (src.cropX / 2); uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; int width = dst.cropWidth; int height = dst.cropHeight; if (vFlip) { height = -height; } // YV12 is the same as I420, with the U and V planes swapped return libyuv::I420ToARGB(srcY, strideY, srcV, strideV, srcU, strideU, dstBuffer, dst.strideBytes, width, height); } int DoConversion(const BufferSpec& src, const BufferSpec& dst, bool v_flip) { ConverterFunction func = GetConverterForDrmFormat(src.drmFormat); if (!func) { // GetConverterForDrmFormat should've logged the issue for us. return -1; } return func(src, dst, v_flip); } int DoCopy(const BufferSpec& src, const BufferSpec& dst, bool v_flip) { ATRACE_CALL(); // Point to the upper left corner of the crop rectangle uint8_t* srcBuffer = src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes; uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; int width = src.cropWidth; int height = src.cropHeight; if (v_flip) { height = -height; } // HAL formats are named based on the order of the pixel components on the // byte stream, while libyuv formats are named based on the order of those // pixel components in an integer written from left to right. So // libyuv::FOURCC_ARGB is equivalent to HAL_PIXEL_FORMAT_BGRA_8888. auto ret = libyuv::ARGBCopy(srcBuffer, src.strideBytes, dstBuffer, dst.strideBytes, width, height); return ret; } int DoRotation(const BufferSpec& src, const BufferSpec& dst, libyuv::RotationMode rotation, bool v_flip) { ATRACE_CALL(); // Point to the upper left corner of the crop rectangles uint8_t* srcBuffer = src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes; uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; int width = src.cropWidth; int height = src.cropHeight; if (v_flip) { height = -height; } return libyuv::ARGBRotate(srcBuffer, src.strideBytes, dstBuffer, dst.strideBytes, width, height, rotation); } int DoScaling(const BufferSpec& src, const BufferSpec& dst, bool v_flip) { ATRACE_CALL(); // Point to the upper left corner of the crop rectangles uint8_t* srcBuffer = src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes; uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; int srcWidth = src.cropWidth; int srcHeight = src.cropHeight; int dstWidth = dst.cropWidth; int dstHeight = dst.cropHeight; if (v_flip) { srcHeight = -srcHeight; } return libyuv::ARGBScale(srcBuffer, src.strideBytes, srcWidth, srcHeight, dstBuffer, dst.strideBytes, dstWidth, dstHeight, libyuv::kFilterBilinear); } int DoAttenuation(const BufferSpec& src, const BufferSpec& dst, bool v_flip) { ATRACE_CALL(); // Point to the upper left corner of the crop rectangles uint8_t* srcBuffer = src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes; uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; int width = dst.cropWidth; int height = dst.cropHeight; if (v_flip) { height = -height; } return libyuv::ARGBAttenuate(srcBuffer, src.strideBytes, dstBuffer, dst.strideBytes, width, height); } int DoBlending(const BufferSpec& src, const BufferSpec& dst, bool v_flip) { ATRACE_CALL(); // Point to the upper left corner of the crop rectangles uint8_t* srcBuffer = src.buffer + src.cropY * src.strideBytes + src.cropX * src.sampleBytes; uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; int width = dst.cropWidth; int height = dst.cropHeight; if (v_flip) { height = -height; } // libyuv's ARGB format is hwcomposer's BGRA format, since blending only cares // for the position of alpha in the pixel and not the position of the colors // this function is perfectly usable. return libyuv::ARGBBlend(srcBuffer, src.strideBytes, dstBuffer, dst.strideBytes, dstBuffer, dst.strideBytes, width, height); } std::optional GetBufferSpec(GrallocBuffer& buffer, GrallocBufferView& bufferView, const hwc_rect_t& bufferCrop) { auto bufferFormatOpt = buffer.GetDrmFormat(); if (!bufferFormatOpt) { ALOGE("Failed to get gralloc buffer format."); return std::nullopt; } uint32_t bufferFormat = *bufferFormatOpt; auto bufferWidthOpt = buffer.GetWidth(); if (!bufferWidthOpt) { ALOGE("Failed to get gralloc buffer width."); return std::nullopt; } uint32_t bufferWidth = *bufferWidthOpt; auto bufferHeightOpt = buffer.GetHeight(); if (!bufferHeightOpt) { ALOGE("Failed to get gralloc buffer height."); return std::nullopt; } uint32_t bufferHeight = *bufferHeightOpt; uint8_t* bufferData = nullptr; uint32_t bufferStrideBytes = 0; std::optional bufferYCbCrData; if (bufferFormat == DRM_FORMAT_NV12 || bufferFormat == DRM_FORMAT_NV21 || bufferFormat == DRM_FORMAT_YVU420) { bufferYCbCrData = bufferView.GetYCbCr(); if (!bufferYCbCrData) { ALOGE("%s failed to get raw ycbcr from view.", __FUNCTION__); return std::nullopt; } } else { auto bufferDataOpt = bufferView.Get(); if (!bufferDataOpt) { ALOGE("%s failed to lock gralloc buffer.", __FUNCTION__); return std::nullopt; } bufferData = reinterpret_cast(*bufferDataOpt); auto bufferStrideBytesOpt = buffer.GetMonoPlanarStrideBytes(); if (!bufferStrideBytesOpt) { ALOGE("%s failed to get plane stride.", __FUNCTION__); return std::nullopt; } bufferStrideBytes = *bufferStrideBytesOpt; } return BufferSpec(bufferData, bufferYCbCrData, bufferWidth, bufferHeight, bufferCrop.left, bufferCrop.top, bufferCrop.right - bufferCrop.left, bufferCrop.bottom - bufferCrop.top, bufferFormat, bufferStrideBytes, GetDrmFormatBytesPerPixel(bufferFormat)); } } // namespace GuestComposer::GuestComposer(DrmPresenter* drmPresenter) : mDrmPresenter(drmPresenter) {} HWC2::Error GuestComposer::init() { DEBUG_LOG("%s", __FUNCTION__); return HWC2::Error::None; } HWC2::Error GuestComposer::onDisplayCreate(Display* display) { hwc2_display_t displayId = display->getId(); hwc2_config_t displayConfigId; int32_t displayWidth; int32_t displayHeight; HWC2::Error error = display->getActiveConfig(&displayConfigId); if (error != HWC2::Error::None) { ALOGE("%s: display:%" PRIu64 " has no active config", __FUNCTION__, displayId); return error; } error = display->getDisplayAttributeEnum( displayConfigId, HWC2::Attribute::Width, &displayWidth); if (error != HWC2::Error::None) { ALOGE("%s: display:%" PRIu64 " failed to get width", __FUNCTION__, displayId); return error; } error = display->getDisplayAttributeEnum( displayConfigId, HWC2::Attribute::Height, &displayHeight); if (error != HWC2::Error::None) { ALOGE("%s: display:%" PRIu64 " failed to get height", __FUNCTION__, displayId); return error; } auto it = mDisplayInfos.find(displayId); if (it != mDisplayInfos.end()) { ALOGE("%s: display:%" PRIu64 " already created?", __FUNCTION__, displayId); } GuestComposerDisplayInfo& displayInfo = mDisplayInfos[displayId]; uint32_t bufferStride; buffer_handle_t bufferHandle; auto status = GraphicBufferAllocator::get().allocate( displayWidth, // displayHeight, // PIXEL_FORMAT_RGBA_8888, // /*layerCount=*/1, // GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_SW_READ_OFTEN | GraphicBuffer::USAGE_SW_WRITE_OFTEN, // &bufferHandle, // &bufferStride, // "RanchuHwc"); if (status != OK) { ALOGE("%s failed to allocate composition buffer for display:%" PRIu64, __FUNCTION__, displayId); return HWC2::Error::NoResources; } displayInfo.compositionResultBuffer = bufferHandle; displayInfo.compositionResultDrmBuffer = std::make_unique( displayInfo.compositionResultBuffer, mDrmPresenter); if (displayId == 0) { auto [flushError, flushSyncFd] = displayInfo.compositionResultDrmBuffer->flushToDisplay(displayId, -1); if (flushError != HWC2::Error::None) { ALOGW( "%s: Initial display flush failed. HWComposer assuming that we are " "running in QEMU without a display and disabling presenting.", __FUNCTION__); mPresentDisabled = true; } } return HWC2::Error::None; } HWC2::Error GuestComposer::onDisplayDestroy(Display* display) { auto displayId = display->getId(); auto it = mDisplayInfos.find(displayId); if (it == mDisplayInfos.end()) { ALOGE("%s: display:%" PRIu64 " missing display buffers?", __FUNCTION__, displayId); return HWC2::Error::BadDisplay; } GuestComposerDisplayInfo& displayInfo = mDisplayInfos[displayId]; GraphicBufferAllocator::get().free(displayInfo.compositionResultBuffer); mDisplayInfos.erase(it); return HWC2::Error::None; } HWC2::Error GuestComposer::getDisplayConfigsFromDeviceConfig( std::vector* configs) { DEBUG_LOG("%s", __FUNCTION__); const auto deviceConfig = cuttlefish::GetDeviceConfig(); for (const auto& deviceDisplayConfig : deviceConfig.display_config()) { DisplayConfig displayConfig = { .width = deviceDisplayConfig.width(), .height = deviceDisplayConfig.height(), .dpiX = deviceDisplayConfig.dpi(), .dpiY = deviceDisplayConfig.dpi(), .refreshRateHz = deviceDisplayConfig.refresh_rate_hz(), }; configs->push_back(displayConfig); } return HWC2::Error::None; } HWC2::Error GuestComposer::getDisplayConfigsFromSystemProp( std::vector* configs) { DEBUG_LOG("%s", __FUNCTION__); static constexpr const char kExternalDisplayProp[] = "hwservicemanager.external.displays"; const auto propString = android::base::GetProperty(kExternalDisplayProp, ""); DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, propString.c_str()); if (propString.empty()) { return HWC2::Error::None; } const std::vector propStringParts = android::base::Split(propString, ","); if (propStringParts.size() % 5 != 0) { ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__, kExternalDisplayProp, propString.c_str()); return HWC2::Error::BadParameter; } std::vector propIntParts; for (const std::string& propStringPart : propStringParts) { int propIntPart; if (!android::base::ParseInt(propStringPart, &propIntPart)) { ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__, kExternalDisplayProp, propString.c_str()); return HWC2::Error::BadParameter; } propIntParts.push_back(propIntPart); } while (!propIntParts.empty()) { DisplayConfig display_config = { .width = propIntParts[1], .height = propIntParts[2], .dpiX = propIntParts[3], .dpiY = propIntParts[3], .refreshRateHz = 160, }; configs->push_back(display_config); propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5); } return HWC2::Error::None; } HWC2::Error GuestComposer::validateDisplay( Display* display, std::unordered_map* outLayerCompositionChanges) { const auto displayId = display->getId(); DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId); const std::vector& layers = display->getOrderedLayers(); bool fallbackToClientComposition = false; for (Layer* layer : layers) { const auto layerId = layer->getId(); const auto layerCompositionType = layer->getCompositionType(); const auto layerCompositionTypeString = to_string(layerCompositionType); if (layerCompositionType == HWC2::Composition::Invalid) { ALOGE("%s display:%" PRIu64 " layer:%" PRIu64 " has Invalid composition", __FUNCTION__, displayId, layerId); continue; } if (layerCompositionType == HWC2::Composition::Client || layerCompositionType == HWC2::Composition::Cursor || layerCompositionType == HWC2::Composition::Sideband || layerCompositionType == HWC2::Composition::SolidColor) { DEBUG_LOG("%s: display:%" PRIu64 " layer:%" PRIu64 " has composition type %s, falling back to client composition", __FUNCTION__, displayId, layerId, layerCompositionTypeString.c_str()); fallbackToClientComposition = true; break; } if (!canComposeLayer(layer)) { DEBUG_LOG( "%s: display:%" PRIu64 " layer:%" PRIu64 " composition not supported, falling back to client composition", __FUNCTION__, displayId, layerId); fallbackToClientComposition = true; break; } } if (fallbackToClientComposition) { for (Layer* layer : layers) { const auto layerId = layer->getId(); const auto layerCompositionType = layer->getCompositionType(); if (layerCompositionType == HWC2::Composition::Invalid) { continue; } if (layerCompositionType != HWC2::Composition::Client) { DEBUG_LOG("%s display:%" PRIu64 " layer:%" PRIu64 "composition updated to Client", __FUNCTION__, displayId, layerId); (*outLayerCompositionChanges)[layerId] = HWC2::Composition::Client; } } } // We can not draw below a Client (SurfaceFlinger) composed layer. Change all // layers below a Client composed layer to also be Client composed. if (layers.size() > 1) { for (std::size_t layerIndex = layers.size() - 1; layerIndex > 0; layerIndex--) { auto layer = layers[layerIndex]; auto layerCompositionType = layer->getCompositionType(); if (layerCompositionType == HWC2::Composition::Client) { for (std::size_t lowerLayerIndex = 0; lowerLayerIndex < layerIndex; lowerLayerIndex++) { auto lowerLayer = layers[lowerLayerIndex]; auto lowerLayerId = lowerLayer->getId(); auto lowerLayerCompositionType = lowerLayer->getCompositionType(); if (lowerLayerCompositionType != HWC2::Composition::Client) { DEBUG_LOG("%s: display:%" PRIu64 " changing layer:%" PRIu64 " to Client because" "hwcomposer can not draw below the Client composed " "layer:%" PRIu64, __FUNCTION__, displayId, lowerLayerId, layer->getId()); (*outLayerCompositionChanges)[lowerLayerId] = HWC2::Composition::Client; } } } } } return HWC2::Error::None; } std::tuple GuestComposer::presentDisplay( Display* display) { ATRACE_CALL(); const auto displayId = display->getId(); DEBUG_LOG("%s display:%" PRIu64, __FUNCTION__, displayId); if (mPresentDisabled) { return std::make_tuple(HWC2::Error::None, base::unique_fd()); } auto it = mDisplayInfos.find(displayId); if (it == mDisplayInfos.end()) { ALOGE("%s: display:%" PRIu64 " not found", __FUNCTION__, displayId); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } GuestComposerDisplayInfo& displayInfo = it->second; if (displayInfo.compositionResultBuffer == nullptr) { ALOGE("%s: display:%" PRIu64 " missing composition result buffer", __FUNCTION__, displayId); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } std::optional compositionResultBufferOpt = mGralloc.Import(displayInfo.compositionResultBuffer); if (!compositionResultBufferOpt) { ALOGE("%s: display:%" PRIu64 " failed to import buffer", __FUNCTION__, displayId); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } std::optional compositionResultBufferWidthOpt = compositionResultBufferOpt->GetWidth(); if (!compositionResultBufferWidthOpt) { ALOGE("%s: display:%" PRIu64 " failed to query buffer width", __FUNCTION__, displayId); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } std::optional compositionResultBufferHeightOpt = compositionResultBufferOpt->GetHeight(); if (!compositionResultBufferHeightOpt) { ALOGE("%s: display:%" PRIu64 " failed to query buffer height", __FUNCTION__, displayId); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } std::optional compositionResultBufferStrideOpt = compositionResultBufferOpt->GetMonoPlanarStrideBytes(); if (!compositionResultBufferStrideOpt) { ALOGE("%s: display:%" PRIu64 " failed to query buffer stride", __FUNCTION__, displayId); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } std::optional compositionResultBufferViewOpt = compositionResultBufferOpt->Lock(); if (!compositionResultBufferViewOpt) { ALOGE("%s: display:%" PRIu64 " failed to get buffer view", __FUNCTION__, displayId); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } const std::optional compositionResultBufferDataOpt = compositionResultBufferViewOpt->Get(); if (!compositionResultBufferDataOpt) { ALOGE("%s: display:%" PRIu64 " failed to get buffer data", __FUNCTION__, displayId); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } uint32_t compositionResultBufferWidth = *compositionResultBufferWidthOpt; uint32_t compositionResultBufferHeight = *compositionResultBufferHeightOpt; uint32_t compositionResultBufferStride = *compositionResultBufferStrideOpt; uint8_t* compositionResultBufferData = reinterpret_cast(*compositionResultBufferDataOpt); const std::vector& layers = display->getOrderedLayers(); const bool noOpComposition = layers.empty(); const bool allLayersClientComposed = std::all_of( layers.begin(), // layers.end(), // [](const Layer* layer) { return layer->getCompositionType() == HWC2::Composition::Client; }); if (noOpComposition) { ALOGW("%s: display:%" PRIu64 " empty composition", __FUNCTION__, displayId); } else if (allLayersClientComposed) { auto clientTargetBufferOpt = mGralloc.Import(display->waitAndGetClientTargetBuffer()); if (!clientTargetBufferOpt) { ALOGE("%s: failed to import client target buffer.", __FUNCTION__); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } GrallocBuffer& clientTargetBuffer = *clientTargetBufferOpt; auto clientTargetBufferViewOpt = clientTargetBuffer.Lock(); if (!clientTargetBufferViewOpt) { ALOGE("%s: failed to lock client target buffer.", __FUNCTION__); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } GrallocBufferView& clientTargetBufferView = *clientTargetBufferViewOpt; auto clientTargetPlaneLayoutsOpt = clientTargetBuffer.GetPlaneLayouts(); if (!clientTargetPlaneLayoutsOpt) { ALOGE("Failed to get client target buffer plane layouts."); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } auto& clientTargetPlaneLayouts = *clientTargetPlaneLayoutsOpt; if (clientTargetPlaneLayouts.size() != 1) { ALOGE("Unexpected number of plane layouts for client target buffer."); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } std::size_t clientTargetPlaneSize = clientTargetPlaneLayouts[0].totalSizeInBytes; auto clientTargetDataOpt = clientTargetBufferView.Get(); if (!clientTargetDataOpt) { ALOGE("%s failed to lock gralloc buffer.", __FUNCTION__); return std::make_tuple(HWC2::Error::NoResources, base::unique_fd()); } auto* clientTargetData = reinterpret_cast(*clientTargetDataOpt); std::memcpy(compositionResultBufferData, clientTargetData, clientTargetPlaneSize); } else { for (Layer* layer : layers) { const auto layerId = layer->getId(); const auto layerCompositionType = layer->getCompositionType(); if (layerCompositionType != HWC2::Composition::Device) { continue; } HWC2::Error error = composeLayerInto(layer, // compositionResultBufferData, // compositionResultBufferWidth, // compositionResultBufferHeight, // compositionResultBufferStride, // 4); if (error != HWC2::Error::None) { ALOGE("%s: display:%" PRIu64 " failed to compose layer:%" PRIu64, __FUNCTION__, displayId, layerId); return std::make_tuple(error, base::unique_fd()); } } } if (display->hasColorTransform()) { const ColorTransformWithMatrix colorTransform = display->getColorTransform(); HWC2::Error error = applyColorTransformToRGBA(colorTransform, // compositionResultBufferData, // compositionResultBufferWidth, // compositionResultBufferHeight, // compositionResultBufferStride); if (error != HWC2::Error::None) { ALOGE("%s: display:%" PRIu64 " failed to apply color transform", __FUNCTION__, displayId); return std::make_tuple(error, base::unique_fd()); } } DEBUG_LOG("%s display:%" PRIu64 " flushing drm buffer", __FUNCTION__, displayId); auto [error, outRetireFence] = displayInfo.compositionResultDrmBuffer->flushToDisplay( static_cast(displayId), -1); if (error != HWC2::Error::None) { ALOGE("%s: display:%" PRIu64 " failed to flush drm buffer" PRIu64, __FUNCTION__, displayId); } return std::make_tuple(error, std::move(outRetireFence)); } bool GuestComposer::canComposeLayer(Layer* layer) { buffer_handle_t bufferHandle = layer->getBuffer().getBuffer(); if (bufferHandle == nullptr) { ALOGW("%s received a layer with a null handle", __FUNCTION__); return false; } auto bufferOpt = mGralloc.Import(bufferHandle); if (!bufferOpt) { ALOGE("Failed to import layer buffer."); return false; } GrallocBuffer& buffer = *bufferOpt; auto bufferFormatOpt = buffer.GetDrmFormat(); if (!bufferFormatOpt) { ALOGE("Failed to get layer buffer format."); return false; } uint32_t bufferFormat = *bufferFormatOpt; if (!IsDrmFormatSupported(bufferFormat)) { return false; } return true; } HWC2::Error GuestComposer::composeLayerInto( Layer* srcLayer, std::uint8_t* dstBuffer, std::uint32_t dstBufferWidth, std::uint32_t dstBufferHeight, std::uint32_t dstBufferStrideBytes, std::uint32_t dstBufferBytesPerPixel) { ATRACE_CALL(); libyuv::RotationMode rotation = GetRotationFromTransform(srcLayer->getTransform()); auto srcBufferOpt = mGralloc.Import(srcLayer->waitAndGetBuffer()); if (!srcBufferOpt) { ALOGE("%s: failed to import layer buffer.", __FUNCTION__); return HWC2::Error::NoResources; } GrallocBuffer& srcBuffer = *srcBufferOpt; auto srcBufferViewOpt = srcBuffer.Lock(); if (!srcBufferViewOpt) { ALOGE("%s: failed to lock import layer buffer.", __FUNCTION__); return HWC2::Error::NoResources; } GrallocBufferView& srcBufferView = *srcBufferViewOpt; hwc_rect_t srcLayerCrop = srcLayer->getSourceCropInt(); hwc_rect_t srcLayerDisplayFrame = srcLayer->getDisplayFrame(); auto srcLayerSpecOpt = GetBufferSpec(srcBuffer, srcBufferView, srcLayerCrop); if (!srcLayerSpecOpt) { return HWC2::Error::NoResources; } BufferSpec srcLayerSpec = *srcLayerSpecOpt; // TODO(jemoreira): Remove the hardcoded fomat. bool needsConversion = srcLayerSpec.drmFormat != DRM_FORMAT_XBGR8888 && srcLayerSpec.drmFormat != DRM_FORMAT_ABGR8888; bool needsScaling = LayerNeedsScaling(*srcLayer); bool needsRotation = rotation != libyuv::kRotate0; bool needsTranspose = needsRotation && rotation != libyuv::kRotate180; bool needsVFlip = GetVFlipFromTransform(srcLayer->getTransform()); bool needsAttenuation = LayerNeedsAttenuation(*srcLayer); bool needsBlending = LayerNeedsBlending(*srcLayer); bool needsCopy = !(needsConversion || needsScaling || needsRotation || needsVFlip || needsAttenuation || needsBlending); BufferSpec dstLayerSpec( dstBuffer, /*buffer_ycbcr=*/std::nullopt, dstBufferWidth, dstBufferHeight, srcLayerDisplayFrame.left, srcLayerDisplayFrame.top, srcLayerDisplayFrame.right - srcLayerDisplayFrame.left, srcLayerDisplayFrame.bottom - srcLayerDisplayFrame.top, DRM_FORMAT_XBGR8888, dstBufferStrideBytes, dstBufferBytesPerPixel); // Add the destination layer to the bottom of the buffer stack std::vector dstBufferStack(1, dstLayerSpec); // If more than operation is to be performed, a temporary buffer is needed for // each additional operation // N operations need N destination buffers, the destination layer (the // framebuffer) is one of them, so only N-1 temporary buffers are needed. // Vertical flip is not taken into account because it can be done together // with any other operation. int neededScratchBuffers = (needsConversion ? 1 : 0) + (needsScaling ? 1 : 0) + (needsRotation ? 1 : 0) + (needsAttenuation ? 1 : 0) + (needsBlending ? 1 : 0) + (needsCopy ? 1 : 0) - 1; int mScratchBufferWidth = srcLayerDisplayFrame.right - srcLayerDisplayFrame.left; int mScratchBufferHeight = srcLayerDisplayFrame.bottom - srcLayerDisplayFrame.top; int mScratchBufferStrideBytes = AlignToPower2(mScratchBufferWidth * dstBufferBytesPerPixel, 4); int mScratchBufferSizeBytes = mScratchBufferHeight * mScratchBufferStrideBytes; for (int i = 0; i < neededScratchBuffers; i++) { BufferSpec mScratchBufferspec( getRotatingScratchBuffer(mScratchBufferSizeBytes, i), mScratchBufferWidth, mScratchBufferHeight, mScratchBufferStrideBytes); dstBufferStack.push_back(mScratchBufferspec); } // Conversion and scaling should always be the first operations, so that every // other operation works on equally sized frames (guaranteed to fit in the // scratch buffers). // TODO(jemoreira): We are converting to ARGB as the first step under the // assumption that scaling ARGB is faster than scaling I420 (the most common). // This should be confirmed with testing. if (needsConversion) { BufferSpec& dstBufferSpec = dstBufferStack.back(); if (needsScaling || needsTranspose) { // If a rotation or a scaling operation are needed the dimensions at the // top of the buffer stack are wrong (wrong sizes for scaling, swapped // width and height for 90 and 270 rotations). // Make width and height match the crop sizes on the source int srcWidth = srcLayerSpec.cropWidth; int srcHeight = srcLayerSpec.cropHeight; int dst_stride_bytes = AlignToPower2(srcWidth * dstBufferBytesPerPixel, 4); size_t needed_size = dst_stride_bytes * srcHeight; dstBufferSpec.width = srcWidth; dstBufferSpec.height = srcHeight; // Adjust the stride accordingly dstBufferSpec.strideBytes = dst_stride_bytes; // Crop sizes also need to be adjusted dstBufferSpec.cropWidth = srcWidth; dstBufferSpec.cropHeight = srcHeight; // cropX and y are fine at 0, format is already set to match destination // In case of a scale, the source frame may be bigger than the default tmp // buffer size dstBufferSpec.buffer = getSpecialScratchBuffer(needed_size); } int retval = DoConversion(srcLayerSpec, dstBufferSpec, needsVFlip); if (retval) { ALOGE("Got error code %d from DoConversion function", retval); } needsVFlip = false; srcLayerSpec = dstBufferSpec; dstBufferStack.pop_back(); } if (needsScaling) { BufferSpec& dstBufferSpec = dstBufferStack.back(); if (needsTranspose) { // If a rotation is needed, the temporary buffer has the correct size but // needs to be transposed and have its stride updated accordingly. The // crop sizes also needs to be transposed, but not the x and y since they // are both zero in a temporary buffer (and it is a temporary buffer // because a rotation will be performed next). std::swap(dstBufferSpec.width, dstBufferSpec.height); std::swap(dstBufferSpec.cropWidth, dstBufferSpec.cropHeight); // TODO (jemoreira): Aligment (To align here may cause the needed size to // be bigger than the buffer, so care should be taken) dstBufferSpec.strideBytes = dstBufferSpec.width * dstBufferBytesPerPixel; } int retval = DoScaling(srcLayerSpec, dstBufferSpec, needsVFlip); needsVFlip = false; if (retval) { ALOGE("Got error code %d from DoScaling function", retval); } srcLayerSpec = dstBufferSpec; dstBufferStack.pop_back(); } if (needsRotation) { int retval = DoRotation(srcLayerSpec, dstBufferStack.back(), rotation, needsVFlip); needsVFlip = false; if (retval) { ALOGE("Got error code %d from DoTransform function", retval); } srcLayerSpec = dstBufferStack.back(); dstBufferStack.pop_back(); } if (needsAttenuation) { int retval = DoAttenuation(srcLayerSpec, dstBufferStack.back(), needsVFlip); needsVFlip = false; if (retval) { ALOGE("Got error code %d from DoBlending function", retval); } srcLayerSpec = dstBufferStack.back(); dstBufferStack.pop_back(); } if (needsCopy) { int retval = DoCopy(srcLayerSpec, dstBufferStack.back(), needsVFlip); needsVFlip = false; if (retval) { ALOGE("Got error code %d from DoBlending function", retval); } srcLayerSpec = dstBufferStack.back(); dstBufferStack.pop_back(); } // Blending (if needed) should always be the last operation, so that it reads // and writes in the destination layer and not some temporary buffer. if (needsBlending) { int retval = DoBlending(srcLayerSpec, dstBufferStack.back(), needsVFlip); needsVFlip = false; if (retval) { ALOGE("Got error code %d from DoBlending function", retval); } // Don't need to assign destination to source in the last one dstBufferStack.pop_back(); } return HWC2::Error::None; } namespace { static constexpr const std::array kInvertColorMatrix = { // clang-format off -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, // clang-format on }; // Returns a color matrix that can be used with libyuv by converting values // in -1 to 1 into -64 to 64 and transposing. std::array ToLibyuvColorMatrix( const std::array& in) { std::array out; for (int r = 0; r < 4; r++) { for (int c = 0; c < 4; c++) { int indexIn = (4 * r) + c; int indexOut = (4 * c) + r; out[indexOut] = std::max( -128, std::min(127, static_cast(in[indexIn] * 64.0f + 0.5f))); } } return out; } } // namespace HWC2::Error GuestComposer::applyColorTransformToRGBA( const ColorTransformWithMatrix& transform, // std::uint8_t* buffer, // std::uint32_t bufferWidth, // std::uint32_t bufferHeight, // std::uint32_t bufferStrideBytes) { ATRACE_CALL(); if (transform.transformType == ColorTransform::ARBITRARY_MATRIX) { if (!transform.transformMatrixOpt.has_value()) { ALOGE("%s: color transform matrix missing", __FUNCTION__); return HWC2::Error::BadParameter; } const auto& transformMatrix = *transform.transformMatrixOpt; const auto transformMatrixLibyuv = ToLibyuvColorMatrix(transformMatrix); libyuv::ARGBColorMatrix(buffer, bufferStrideBytes, // in buffer params buffer, bufferStrideBytes, // out buffer params transformMatrixLibyuv.data(), // bufferWidth, // bufferHeight); } else if (transform.transformType == ColorTransform::VALUE_INVERSE) { const auto transformMatrixLibyuv = ToLibyuvColorMatrix(kInvertColorMatrix); libyuv::ARGBColorMatrix(buffer, bufferStrideBytes, // in buffer params buffer, bufferStrideBytes, // out buffer params transformMatrixLibyuv.data(), // bufferWidth, // bufferHeight); } else if (transform.transformType == ColorTransform::GRAYSCALE) { libyuv::ARGBGrayTo(buffer, bufferStrideBytes, // in buffer params buffer, bufferStrideBytes, // out buffer params bufferWidth, // bufferHeight); } else { const auto transformTypeString = toString(transform.transformType); ALOGE("%s: unhandled color transform type %s", __FUNCTION__, transformTypeString.c_str()); return HWC2::Error::BadParameter; } return HWC2::Error::None; } uint8_t* GuestComposer::getRotatingScratchBuffer(std::size_t neededSize, std::uint32_t order) { static constexpr const int kNumScratchBufferPieces = 2; std::size_t totalNeededSize = neededSize * kNumScratchBufferPieces; if (mScratchBuffer.size() < totalNeededSize) { mScratchBuffer.resize(totalNeededSize); } std::size_t bufferIndex = order % kNumScratchBufferPieces; std::size_t bufferOffset = bufferIndex * neededSize; return &mScratchBuffer[bufferOffset]; } uint8_t* GuestComposer::getSpecialScratchBuffer(size_t neededSize) { if (mSpecialScratchBuffer.size() < neededSize) { mSpecialScratchBuffer.resize(neededSize); } return &mSpecialScratchBuffer[0]; } } // namespace android