/* * Copyright 2022 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 "GuestFrameComposer.h" #include #include #include #include #include #include #include #include #include #include #include "Display.h" #include "DisplayFinder.h" #include "Drm.h" #include "Layer.h" namespace aidl::android::hardware::graphics::composer3::impl { namespace { using ::android::hardware::graphics::common::V1_0::ColorTransform; uint32_t AlignToPower2(uint32_t val, uint8_t align_log) { uint32_t align = 1 << align_log; return ((val + (align - 1)) / align) * align; } bool LayerNeedsScaling(const Layer& layer) { common::Rect crop = layer.getSourceCropInt(); common::Rect 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 = static_cast(layer.getTransform()) & static_cast(common::Transform::ROT_90); return needs_rot ? rot_scale : not_rot_scale; } bool LayerNeedsBlending(const Layer& layer) { return layer.getBlendMode() != common::BlendMode::NONE; } bool LayerNeedsAttenuation(const Layer& layer) { return layer.getBlendMode() == common::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(common::Transform transform) { uint32_t rotation = 0; rotation += (static_cast(transform) & static_cast(common::Transform::ROT_90)) ? 1 : 0; // 1 * ROT90 bit rotation += (static_cast(transform) & static_cast(common::Transform::FLIP_H)) ? 2 : 0; // 2 * VFLIP bit return static_cast(90 * rotation); } bool GetVFlipFromTransform(common::Transform transform) { // vertical flip xor horizontal flip bool hasVFlip = static_cast(transform) & static_cast(common::Transform::FLIP_V); bool hasHFlip = static_cast(transform) & static_cast(common::Transform::FLIP_H); return hasVFlip ^ hasHFlip; } struct BufferSpec { uint8_t* buffer; std::optional buffer_ycbcr; uint32_t width; uint32_t height; uint32_t cropX; uint32_t cropY; uint32_t cropWidth; uint32_t cropHeight; uint32_t drmFormat; uint32_t strideBytes; uint32_t sampleBytes; BufferSpec() = default; BufferSpec(uint8_t* buffer, std::optional buffer_ycbcr, uint32_t width, uint32_t height, uint32_t cropX, uint32_t cropY, uint32_t cropWidth, uint32_t cropHeight, uint32_t drmFormat, uint32_t strideBytes, uint32_t 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, uint32_t width, uint32_t height, uint32_t 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 DoFill(const BufferSpec& dst, const Color& color) { ATRACE_CALL(); const uint8_t r = static_cast(color.r * 255.0f); const uint8_t g = static_cast(color.g * 255.0f); const uint8_t b = static_cast(color.b * 255.0f); const uint8_t a = static_cast(color.a * 255.0f); const uint32_t rgba = static_cast(r) | static_cast(g) << 8 | static_cast(b) << 16 | static_cast(a) << 24; // Point to the upper left corner of the crop rectangle. uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; libyuv::SetPlane(dstBuffer, static_cast(dst.strideBytes), static_cast(dst.cropWidth), static_cast(dst.cropHeight), rgba); return 0; } 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; const int srcStrideBytes = static_cast(src.strideBytes); uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; const int dstStrideBytes = static_cast(dst.strideBytes); int width = static_cast(src.cropWidth); int height = static_cast(src.cropHeight); if (vFlip) { height = -height; } return libyuv::RGB565ToARGB(srcBuffer, srcStrideBytes, // dstBuffer, dstStrideBytes, // 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); const int strideYBytes = static_cast(srcBufferYCbCr.ystride); uint8_t* srcU = reinterpret_cast(srcBufferYCbCr.cb); const int strideUBytes = static_cast(srcBufferYCbCr.cstride); uint8_t* srcV = reinterpret_cast(srcBufferYCbCr.cr); const int strideVBytes = static_cast(srcBufferYCbCr.cstride); // Adjust for crop srcY += src.cropY * srcBufferYCbCr.ystride + src.cropX; srcV += (src.cropY / 2) * srcBufferYCbCr.cstride + (src.cropX / 2); srcU += (src.cropY / 2) * srcBufferYCbCr.cstride + (src.cropX / 2); uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; const int dstStrideBytes = static_cast(dst.strideBytes); int width = static_cast(dst.cropWidth); int height = static_cast(dst.cropHeight); if (vFlip) { height = -height; } // YV12 is the same as I420, with the U and V planes swapped return libyuv::I420ToARGB(srcY, strideYBytes, // srcV, strideVBytes, // srcU, strideUBytes, // dstBuffer, dstStrideBytes, 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; const int srcStrideBytes = static_cast(src.strideBytes); uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; const int dstStrideBytes = static_cast(dst.strideBytes); int width = static_cast(src.cropWidth); int height = static_cast(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, srcStrideBytes, // dstBuffer, dstStrideBytes, // 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; const int srcStrideBytes = static_cast(src.strideBytes); uint8_t* dstBuffer = dst.buffer + dst.cropY * dst.strideBytes + dst.cropX * dst.sampleBytes; const int dstStrideBytes = static_cast(dst.strideBytes); int width = static_cast(src.cropWidth); int height = static_cast(src.cropHeight); if (v_flip) { height = -height; } return libyuv::ARGBRotate(srcBuffer, srcStrideBytes, // dstBuffer, dstStrideBytes, // 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; const int srcStrideBytes = static_cast(src.strideBytes); const int dstStrideBytes = static_cast(dst.strideBytes); const int srcWidth = static_cast(src.cropWidth); int srcHeight = static_cast(src.cropHeight); const int dstWidth = static_cast(dst.cropWidth); const int dstHeight = static_cast(dst.cropHeight); if (v_flip) { srcHeight = -srcHeight; } return libyuv::ARGBScale(srcBuffer, srcStrideBytes, srcWidth, srcHeight, dstBuffer, dstStrideBytes, 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; const int srcStrideBytes = static_cast(src.strideBytes); const int dstStrideBytes = static_cast(dst.strideBytes); const int width = static_cast(dst.cropWidth); int height = static_cast(dst.cropHeight); if (v_flip) { height = -height; } return libyuv::ARGBAttenuate(srcBuffer, srcStrideBytes, // dstBuffer, dstStrideBytes, // 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; const int srcStrideBytes = static_cast(src.strideBytes); const int dstStrideBytes = static_cast(dst.strideBytes); const int width = static_cast(dst.cropWidth); int height = static_cast(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, srcStrideBytes, // dstBuffer, dstStrideBytes, // dstBuffer, dstStrideBytes, // width, height); } std::optional GetBufferSpec(GrallocBuffer& buffer, GrallocBufferView& bufferView, const common::Rect& 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; } uint32_t bufferCropX = static_cast(bufferCrop.left); uint32_t bufferCropY = static_cast(bufferCrop.top); uint32_t bufferCropWidth = static_cast(bufferCrop.right - bufferCrop.left); uint32_t bufferCropHeight = static_cast(bufferCrop.bottom - bufferCrop.top); return BufferSpec(bufferData, bufferYCbCrData, bufferWidth, bufferHeight, bufferCropX, bufferCropY, bufferCropWidth, bufferCropHeight, bufferFormat, bufferStrideBytes, GetDrmFormatBytesPerPixel(bufferFormat)); } } // namespace HWC3::Error GuestFrameComposer::init() { DEBUG_LOG("%s", __FUNCTION__); HWC3::Error error = mDrmClient.init(); if (error != HWC3::Error::None) { ALOGE("%s: failed to initialize DrmClient", __FUNCTION__); return error; } return HWC3::Error::None; } HWC3::Error GuestFrameComposer::registerOnHotplugCallback(const HotplugCallback& cb) { return mDrmClient.registerOnHotplugCallback(cb); return HWC3::Error::None; } HWC3::Error GuestFrameComposer::unregisterOnHotplugCallback() { return mDrmClient.unregisterOnHotplugCallback(); } HWC3::Error GuestFrameComposer::onDisplayCreate(Display* display) { const uint32_t displayId = static_cast(display->getId()); int32_t displayConfigId; int32_t displayWidth; int32_t displayHeight; HWC3::Error error = display->getActiveConfig(&displayConfigId); if (error != HWC3::Error::None) { ALOGE("%s: display:%" PRIu32 " has no active config", __FUNCTION__, displayId); return error; } error = display->getDisplayAttribute(displayConfigId, DisplayAttribute::WIDTH, &displayWidth); if (error != HWC3::Error::None) { ALOGE("%s: display:%" PRIu32 " failed to get width", __FUNCTION__, displayId); return error; } error = display->getDisplayAttribute(displayConfigId, DisplayAttribute::HEIGHT, &displayHeight); if (error != HWC3::Error::None) { ALOGE("%s: display:%" PRIu32 " failed to get height", __FUNCTION__, displayId); return error; } auto it = mDisplayInfos.find(displayId); if (it != mDisplayInfos.end()) { ALOGE("%s: display:%" PRIu32 " already created?", __FUNCTION__, displayId); } DisplayInfo& displayInfo = mDisplayInfos[displayId]; displayInfo.swapchain = DrmSwapchain::create(static_cast(displayWidth), static_cast(displayHeight), ::android::GraphicBuffer::USAGE_HW_COMPOSER | ::android::GraphicBuffer::USAGE_SW_READ_OFTEN | ::android::GraphicBuffer::USAGE_SW_WRITE_OFTEN, &mDrmClient); if (displayId == 0) { auto compositionResult = displayInfo.swapchain->getNextImage(); auto [flushError, flushSyncFd] = mDrmClient.flushToDisplay(displayId, compositionResult->getDrmBuffer(), -1); if (flushError != HWC3::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; } else { compositionResult->markAsInUse(std::move(flushSyncFd)); } } std::optional> edid = mDrmClient.getEdid(displayId); if (edid) { display->setEdid(*edid); } return HWC3::Error::None; } HWC3::Error GuestFrameComposer::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 HWC3::Error::BadDisplay; } mDisplayInfos.erase(it); return HWC3::Error::None; } HWC3::Error GuestFrameComposer::onDisplayClientTargetSet(Display*) { return HWC3::Error::None; } HWC3::Error GuestFrameComposer::onActiveConfigChange(Display* /*display*/) { return HWC3::Error::None; }; HWC3::Error GuestFrameComposer::getDisplayConfigsFromSystemProp( std::vector* configs) { DEBUG_LOG("%s", __FUNCTION__); std::vector propIntParts; parseExternalDisplaysFromProperties(propIntParts); 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 HWC3::Error::None; } HWC3::Error GuestFrameComposer::validateDisplay(Display* display, DisplayChanges* outChanges) { 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 = toString(layerCompositionType); if (layerCompositionType == Composition::INVALID) { ALOGE("%s display:%" PRIu64 " layer:%" PRIu64 " has Invalid composition", __FUNCTION__, displayId, layerId); continue; } if (layerCompositionType == Composition::CLIENT || layerCompositionType == Composition::CURSOR || layerCompositionType == Composition::SIDEBAND) { 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 (layerCompositionType == Composition::DISPLAY_DECORATION) { return HWC3::Error::Unsupported; } 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 == Composition::INVALID) { continue; } if (layerCompositionType != Composition::CLIENT) { DEBUG_LOG("%s display:%" PRIu64 " layer:%" PRIu64 "composition updated to Client", __FUNCTION__, displayId, layerId); outChanges->addLayerCompositionChange(displayId, layerId, 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 == 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 != 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()); outChanges->addLayerCompositionChange(displayId, lowerLayerId, Composition::CLIENT); } } } } } return HWC3::Error::None; } HWC3::Error GuestFrameComposer::presentDisplay( Display* display, ::android::base::unique_fd* outDisplayFence, std::unordered_map* /*outLayerFences*/) { const uint32_t displayId = static_cast(display->getId()); DEBUG_LOG("%s display:%" PRIu32, __FUNCTION__, displayId); if (mPresentDisabled) { return HWC3::Error::None; } auto it = mDisplayInfos.find(displayId); if (it == mDisplayInfos.end()) { ALOGE("%s: display:%" PRIu32 " not found", __FUNCTION__, displayId); return HWC3::Error::NoResources; } DisplayInfo& displayInfo = it->second; auto compositionResult = displayInfo.swapchain->getNextImage(); compositionResult->wait(); if (compositionResult->getBuffer() == nullptr) { ALOGE("%s: display:%" PRIu32 " missing composition result buffer", __FUNCTION__, displayId); return HWC3::Error::NoResources; } if (compositionResult->getDrmBuffer() == nullptr) { ALOGE("%s: display:%" PRIu32 " missing composition result drm buffer", __FUNCTION__, displayId); return HWC3::Error::NoResources; } std::optional compositionResultBufferOpt = mGralloc.Import(compositionResult->getBuffer()); if (!compositionResultBufferOpt) { ALOGE("%s: display:%" PRIu32 " failed to import buffer", __FUNCTION__, displayId); return HWC3::Error::NoResources; } std::optional compositionResultBufferWidthOpt = compositionResultBufferOpt->GetWidth(); if (!compositionResultBufferWidthOpt) { ALOGE("%s: display:%" PRIu32 " failed to query buffer width", __FUNCTION__, displayId); return HWC3::Error::NoResources; } std::optional compositionResultBufferHeightOpt = compositionResultBufferOpt->GetHeight(); if (!compositionResultBufferHeightOpt) { ALOGE("%s: display:%" PRIu32 " failed to query buffer height", __FUNCTION__, displayId); return HWC3::Error::NoResources; } std::optional compositionResultBufferStrideOpt = compositionResultBufferOpt->GetMonoPlanarStrideBytes(); if (!compositionResultBufferStrideOpt) { ALOGE("%s: display:%" PRIu32 " failed to query buffer stride", __FUNCTION__, displayId); return HWC3::Error::NoResources; } std::optional compositionResultBufferViewOpt = compositionResultBufferOpt->Lock(); if (!compositionResultBufferViewOpt) { ALOGE("%s: display:%" PRIu32 " failed to get buffer view", __FUNCTION__, displayId); return HWC3::Error::NoResources; } const std::optional compositionResultBufferDataOpt = compositionResultBufferViewOpt->Get(); if (!compositionResultBufferDataOpt) { ALOGE("%s: display:%" PRIu32 " failed to get buffer data", __FUNCTION__, displayId); return HWC3::Error::NoResources; } 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() == Composition::CLIENT; }); if (noOpComposition) { ALOGW("%s: display:%" PRIu32 " 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 HWC3::Error::NoResources; } GrallocBuffer& clientTargetBuffer = *clientTargetBufferOpt; auto clientTargetBufferViewOpt = clientTargetBuffer.Lock(); if (!clientTargetBufferViewOpt) { ALOGE("%s: failed to lock client target buffer.", __FUNCTION__); return HWC3::Error::NoResources; } GrallocBufferView& clientTargetBufferView = *clientTargetBufferViewOpt; auto clientTargetPlaneLayoutsOpt = clientTargetBuffer.GetPlaneLayouts(); if (!clientTargetPlaneLayoutsOpt) { ALOGE("Failed to get client target buffer plane layouts."); return HWC3::Error::NoResources; } auto& clientTargetPlaneLayouts = *clientTargetPlaneLayoutsOpt; if (clientTargetPlaneLayouts.size() != 1) { ALOGE("Unexpected number of plane layouts for client target buffer."); return HWC3::Error::NoResources; } std::size_t clientTargetPlaneSize = static_cast(clientTargetPlaneLayouts[0].totalSizeInBytes); auto clientTargetDataOpt = clientTargetBufferView.Get(); if (!clientTargetDataOpt) { ALOGE("%s failed to lock gralloc buffer.", __FUNCTION__); return HWC3::Error::NoResources; } 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 != Composition::DEVICE && layerCompositionType != Composition::SOLID_COLOR) { continue; } HWC3::Error error = composeLayerInto(displayInfo.compositionIntermediateStorage, // layer, // compositionResultBufferData, // compositionResultBufferWidth, // compositionResultBufferHeight, // compositionResultBufferStride, // 4); if (error != HWC3::Error::None) { ALOGE("%s: display:%" PRIu32 " failed to compose layer:%" PRIu64, __FUNCTION__, displayId, layerId); return error; } } } if (display->hasColorTransform()) { HWC3::Error error = applyColorTransformToRGBA(display->getColorTransform(), // compositionResultBufferData, // compositionResultBufferWidth, // compositionResultBufferHeight, // compositionResultBufferStride); if (error != HWC3::Error::None) { ALOGE("%s: display:%" PRIu32 " failed to apply color transform", __FUNCTION__, displayId); return error; } } DEBUG_LOG("%s display:%" PRIu32 " flushing drm buffer", __FUNCTION__, displayId); auto [error, fence] = mDrmClient.flushToDisplay(displayId, compositionResult->getDrmBuffer(), -1); if (error != HWC3::Error::None) { ALOGE("%s: display:%" PRIu32 " failed to flush drm buffer" PRIu64, __FUNCTION__, displayId); } *outDisplayFence = std::move(fence); compositionResult->markAsInUse(outDisplayFence->ok() ? ::android::base::unique_fd(dup(*outDisplayFence)) : ::android::base::unique_fd()); return error; } bool GuestFrameComposer::canComposeLayer(Layer* layer) { const auto layerCompositionType = layer->getCompositionType(); if (layerCompositionType == Composition::SOLID_COLOR) { return true; } if (layerCompositionType != Composition::DEVICE) { return false; } 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; } HWC3::Error GuestFrameComposer::composeLayerInto( AlternatingImageStorage& compositionIntermediateStorage, 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()); common::Rect srcLayerCrop = srcLayer->getSourceCropInt(); common::Rect srcLayerDisplayFrame = srcLayer->getDisplayFrame(); BufferSpec srcLayerSpec; std::optional srcBufferOpt; std::optional srcBufferViewOpt; const auto srcLayerCompositionType = srcLayer->getCompositionType(); if (srcLayerCompositionType == Composition::DEVICE) { srcBufferOpt = mGralloc.Import(srcLayer->waitAndGetBuffer()); if (!srcBufferOpt) { ALOGE("%s: failed to import layer buffer.", __FUNCTION__); return HWC3::Error::NoResources; } GrallocBuffer& srcBuffer = *srcBufferOpt; srcBufferViewOpt = srcBuffer.Lock(); if (!srcBufferViewOpt) { ALOGE("%s: failed to lock import layer buffer.", __FUNCTION__); return HWC3::Error::NoResources; } GrallocBufferView& srcBufferView = *srcBufferViewOpt; auto srcLayerSpecOpt = GetBufferSpec(srcBuffer, srcBufferView, srcLayerCrop); if (!srcLayerSpecOpt) { return HWC3::Error::NoResources; } srcLayerSpec = *srcLayerSpecOpt; } else if (srcLayerCompositionType == Composition::SOLID_COLOR) { // srcLayerSpec not used by `needsFill` below. } // TODO(jemoreira): Remove the hardcoded fomat. bool needsFill = srcLayerCompositionType == Composition::SOLID_COLOR; bool needsConversion = srcLayerCompositionType == Composition::DEVICE && 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, static_cast(srcLayerDisplayFrame.left), static_cast(srcLayerDisplayFrame.top), static_cast(srcLayerDisplayFrame.right - srcLayerDisplayFrame.left), static_cast(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 neededIntermediateImages = (needsFill ? 1 : 0) + (needsConversion ? 1 : 0) + (needsScaling ? 1 : 0) + (needsRotation ? 1 : 0) + (needsAttenuation ? 1 : 0) + (needsBlending ? 1 : 0) + (needsCopy ? 1 : 0) - 1; uint32_t mScratchBufferWidth = static_cast(srcLayerDisplayFrame.right - srcLayerDisplayFrame.left); uint32_t mScratchBufferHeight = static_cast(srcLayerDisplayFrame.bottom - srcLayerDisplayFrame.top); uint32_t mScratchBufferStrideBytes = AlignToPower2(mScratchBufferWidth * dstBufferBytesPerPixel, 4); uint32_t mScratchBufferSizeBytes = mScratchBufferHeight * mScratchBufferStrideBytes; for (uint32_t i = 0; i < neededIntermediateImages; i++) { BufferSpec mScratchBufferspec( compositionIntermediateStorage.getRotatingScratchBuffer(mScratchBufferSizeBytes, i), mScratchBufferWidth, mScratchBufferHeight, mScratchBufferStrideBytes); dstBufferStack.push_back(mScratchBufferspec); } // Filling, 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) in a common format. if (needsFill) { BufferSpec& dstBufferSpec = dstBufferStack.back(); int retval = DoFill(dstBufferSpec, srcLayer->getColor()); if (retval) { ALOGE("Got error code %d from DoFill function", retval); } srcLayerSpec = dstBufferSpec; dstBufferStack.pop_back(); } // 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 uint32_t srcWidth = srcLayerSpec.cropWidth; uint32_t srcHeight = srcLayerSpec.cropHeight; uint32_t dst_stride_bytes = AlignToPower2(srcWidth * dstBufferBytesPerPixel, 4); uint32_t neededSize = 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 = compositionIntermediateStorage.getSpecialScratchBuffer(neededSize); } 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 HWC3::Error::None; } namespace { // 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 (size_t r = 0; r < 4; r++) { for (size_t c = 0; c < 4; c++) { size_t indexIn = (4 * r) + c; size_t indexOut = (4 * c) + r; out[indexOut] = static_cast( std::max(-128, std::min(127, static_cast(in[indexIn] * 64.0f + 0.5f)))); } } return out; } } // namespace HWC3::Error GuestFrameComposer::applyColorTransformToRGBA( const std::array& transfromMatrix, // std::uint8_t* buffer, // std::uint32_t bufferWidth, // std::uint32_t bufferHeight, // std::uint32_t bufferStrideBytes) { ATRACE_CALL(); const auto transformMatrixLibyuv = ToLibyuvColorMatrix(transfromMatrix); libyuv::ARGBColorMatrix(buffer, static_cast(bufferStrideBytes), // buffer, static_cast(bufferStrideBytes), // transformMatrixLibyuv.data(), // static_cast(bufferWidth), // static_cast(bufferHeight)); return HWC3::Error::None; } } // namespace aidl::android::hardware::graphics::composer3::impl