| /* |
| * 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 "HostComposer.h" |
| |
| #include <EGL/egl.h> |
| #include <EGL/eglext.h> |
| #include <android-base/parseint.h> |
| #include <android-base/properties.h> |
| #include <android-base/strings.h> |
| #include <drm/virtgpu_drm.h> |
| #include <poll.h> |
| #include <sync/sync.h> |
| #include <ui/GraphicBuffer.h> |
| #include <ui/GraphicBufferAllocator.h> |
| #include <ui/GraphicBufferMapper.h> |
| |
| #include "../egl/goldfish_sync.h" |
| #include "Device.h" |
| #include "Display.h" |
| |
| namespace android { |
| namespace { |
| |
| static int getVsyncHzFromProperty() { |
| static constexpr const auto kVsyncProp = "ro.boot.qemu.vsync"; |
| |
| const auto vsyncProp = android::base::GetProperty(kVsyncProp, ""); |
| DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, vsyncProp.c_str()); |
| |
| uint64_t vsyncPeriod; |
| if (!android::base::ParseUint(vsyncProp, &vsyncPeriod)) { |
| ALOGE("%s: failed to parse vsync period '%s', returning default 60", |
| __FUNCTION__, vsyncProp.c_str()); |
| return 60; |
| } |
| |
| return static_cast<int>(vsyncPeriod); |
| } |
| |
| static bool isMinigbmFromProperty() { |
| static constexpr const auto kGrallocProp = "ro.hardware.gralloc"; |
| |
| const auto grallocProp = android::base::GetProperty(kGrallocProp, ""); |
| DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, grallocProp.c_str()); |
| |
| if (grallocProp == "minigbm") { |
| ALOGD("%s: Using minigbm, in minigbm mode.\n", __FUNCTION__); |
| return true; |
| } else { |
| ALOGD("%s: Is not using minigbm, in goldfish mode.\n", __FUNCTION__); |
| return false; |
| } |
| } |
| |
| static bool useAngleFromProperty() { |
| static constexpr const auto kEglProp = "ro.hardware.egl"; |
| |
| const auto eglProp = android::base::GetProperty(kEglProp, ""); |
| DEBUG_LOG("%s: prop value is: %s", __FUNCTION__, eglProp.c_str()); |
| |
| if (eglProp == "angle") { |
| ALOGD("%s: Using ANGLE.\n", __FUNCTION__); |
| return true; |
| } else { |
| ALOGD("%s: Not using ANGLE.\n", __FUNCTION__); |
| return false; |
| } |
| } |
| |
| #define DEFINE_AND_VALIDATE_HOST_CONNECTION \ |
| HostConnection* hostCon = createOrGetHostConnection(); \ |
| if (!hostCon) { \ |
| ALOGE("%s: Failed to get host connection\n", __FUNCTION__); \ |
| return HWC2::Error::NoResources; \ |
| } \ |
| ExtendedRCEncoderContext* rcEnc = hostCon->rcEncoder(); \ |
| if (!rcEnc) { \ |
| ALOGE("%s: Failed to get renderControl encoder context\n", __FUNCTION__); \ |
| return HWC2::Error::NoResources; \ |
| } |
| |
| static std::unique_ptr<HostConnection> sHostCon; |
| |
| static HostConnection* createOrGetHostConnection() { |
| if (!sHostCon) { |
| sHostCon = HostConnection::createUnique(); |
| } |
| return sHostCon.get(); |
| } |
| |
| typedef struct compose_layer { |
| uint32_t cbHandle; |
| hwc2_composition_t composeMode; |
| hwc_rect_t displayFrame; |
| hwc_frect_t crop; |
| int32_t blendMode; |
| float alpha; |
| hwc_color_t color; |
| hwc_transform_t transform; |
| } ComposeLayer; |
| |
| typedef struct compose_device { |
| uint32_t version; |
| uint32_t targetHandle; |
| uint32_t numLayers; |
| struct compose_layer layer[0]; |
| } ComposeDevice; |
| |
| typedef struct compose_device_v2 { |
| uint32_t version; |
| uint32_t displayId; |
| uint32_t targetHandle; |
| uint32_t numLayers; |
| struct compose_layer layer[0]; |
| } ComposeDevice_v2; |
| |
| class ComposeMsg { |
| public: |
| ComposeMsg(uint32_t layerCnt = 0) |
| : mData(sizeof(ComposeDevice) + layerCnt * sizeof(ComposeLayer)) { |
| mComposeDevice = reinterpret_cast<ComposeDevice*>(mData.data()); |
| mLayerCnt = layerCnt; |
| } |
| |
| ComposeDevice* get() { return mComposeDevice; } |
| |
| uint32_t getLayerCnt() { return mLayerCnt; } |
| |
| private: |
| std::vector<uint8_t> mData; |
| uint32_t mLayerCnt; |
| ComposeDevice* mComposeDevice; |
| }; |
| |
| class ComposeMsg_v2 { |
| public: |
| ComposeMsg_v2(uint32_t layerCnt = 0) |
| : mData(sizeof(ComposeDevice_v2) + layerCnt * sizeof(ComposeLayer)) { |
| mComposeDevice = reinterpret_cast<ComposeDevice_v2*>(mData.data()); |
| mLayerCnt = layerCnt; |
| } |
| |
| ComposeDevice_v2* get() { return mComposeDevice; } |
| |
| uint32_t getLayerCnt() { return mLayerCnt; } |
| |
| private: |
| std::vector<uint8_t> mData; |
| uint32_t mLayerCnt; |
| ComposeDevice_v2* mComposeDevice; |
| }; |
| |
| const native_handle_t* AllocateDisplayColorBuffer(int width, int height) { |
| const uint32_t layerCount = 1; |
| const uint64_t graphicBufferId = 0; // not used |
| buffer_handle_t h; |
| uint32_t stride; |
| |
| if (GraphicBufferAllocator::get().allocate( |
| width, height, PIXEL_FORMAT_RGBA_8888, layerCount, |
| (GraphicBuffer::USAGE_HW_COMPOSER | GraphicBuffer::USAGE_HW_RENDER), |
| &h, &stride, graphicBufferId, "EmuHWC2") == OK) { |
| return static_cast<const native_handle_t*>(h); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| void FreeDisplayColorBuffer(const native_handle_t* h) { |
| GraphicBufferAllocator::get().free(h); |
| } |
| |
| } // namespace |
| |
| HWC2::Error HostComposer::init(const HotplugCallback& cb) { |
| mIsMinigbm = isMinigbmFromProperty(); |
| mUseAngle = useAngleFromProperty(); |
| |
| if (mIsMinigbm) { |
| if (!mDrmPresenter.init(cb)) { |
| ALOGE("%s: failed to initialize DrmPresenter", __FUNCTION__); |
| return HWC2::Error::NoResources; |
| } |
| } else { |
| mSyncDeviceFd = goldfish_sync_open(); |
| } |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error HostComposer::createDisplays( |
| Device* device, const AddDisplayToDeviceFunction& addDisplayToDeviceFn) { |
| HWC2::Error error = HWC2::Error::None; |
| |
| error = createPrimaryDisplay(device, addDisplayToDeviceFn); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to create primary display", __FUNCTION__); |
| return error; |
| } |
| |
| error = createSecondaryDisplays(device, addDisplayToDeviceFn); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to create secondary displays", __FUNCTION__); |
| return error; |
| } |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error HostComposer::createPrimaryDisplay( |
| Device* device, const AddDisplayToDeviceFunction& addDisplayToDeviceFn) { |
| HWC2::Error error = HWC2::Error::None; |
| |
| DEFINE_AND_VALIDATE_HOST_CONNECTION |
| hostCon->lock(); |
| int width = rcEnc->rcGetFBParam(rcEnc, FB_WIDTH); |
| int height = rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT); |
| int dpiX = rcEnc->rcGetFBParam(rcEnc, FB_XDPI); |
| int dpiY = rcEnc->rcGetFBParam(rcEnc, FB_YDPI); |
| hostCon->unlock(); |
| |
| int refreshRateHz = getVsyncHzFromProperty(); |
| |
| auto display = std::make_unique<Display>(*device, this, 0); |
| if (display == nullptr) { |
| ALOGE("%s failed to allocate display", __FUNCTION__); |
| return HWC2::Error::NoResources; |
| } |
| |
| auto displayId = display->getId(); |
| |
| error = display->init(width, height, dpiX, dpiY, refreshRateHz); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to initialize display:%" PRIu64, __FUNCTION__, displayId); |
| return error; |
| } |
| |
| error = createHostComposerDisplayInfo(display.get(), /*hostDisplayId=*/0); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to initialize host info for display:%" PRIu64, |
| __FUNCTION__, displayId); |
| return error; |
| } |
| |
| error = addDisplayToDeviceFn(std::move(display)); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to add display:%" PRIu64, __FUNCTION__, displayId); |
| return error; |
| } |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error HostComposer::createDisplay( |
| Device* device, uint32_t displayId, uint32_t width, uint32_t height, |
| uint32_t dpiX, uint32_t dpiY, uint32_t refreshRateHz, |
| const AddDisplayToDeviceFunction& addDisplayToDeviceFn) { |
| HWC2::Error error; |
| Display* display = device->getDisplay(displayId); |
| if (display) { |
| ALOGD("%s display %d already existed, then update", __func__, displayId); |
| } |
| |
| DEFINE_AND_VALIDATE_HOST_CONNECTION |
| hostCon->lock(); |
| if (rcEnc->rcCreateDisplayById(rcEnc, displayId)) { |
| ALOGE("%s host failed to create display %" PRIu32, __func__, displayId); |
| hostCon->unlock(); |
| return HWC2::Error::NoResources; |
| } |
| if (rcEnc->rcSetDisplayPoseDpi(rcEnc, displayId, -1, -1, width, height, dpiX/1000)) { |
| ALOGE("%s host failed to set display %" PRIu32, __func__, displayId); |
| hostCon->unlock(); |
| return HWC2::Error::NoResources; |
| } |
| hostCon->unlock(); |
| |
| std::optional<std::vector<uint8_t>> edid; |
| if (mIsMinigbm) { |
| edid = mDrmPresenter.getEdid(displayId); |
| } |
| if (!display) { |
| auto newDisplay = std::make_unique<Display>(*device, this, displayId); |
| if (newDisplay == nullptr) { |
| ALOGE("%s failed to allocate display", __FUNCTION__); |
| return HWC2::Error::NoResources; |
| } |
| |
| |
| error = newDisplay->init(width, height, dpiX, dpiY, refreshRateHz, edid); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to initialize display:%" PRIu32, __FUNCTION__, |
| displayId); |
| return error; |
| } |
| |
| error = |
| createHostComposerDisplayInfo(newDisplay.get(), displayId); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to initialize host info for display:%" PRIu32, |
| __FUNCTION__, displayId); |
| return error; |
| } |
| |
| error = addDisplayToDeviceFn(std::move(newDisplay)); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to add display:%" PRIu32, __FUNCTION__, displayId); |
| return error; |
| } |
| } else { |
| display->lock(); |
| // update display parameters |
| error = display->updateParameters(width, height, dpiX, dpiY, |
| refreshRateHz, edid); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to update display:%" PRIu32, __FUNCTION__, displayId); |
| display->unlock(); |
| return error; |
| } |
| |
| error = createHostComposerDisplayInfo(display, displayId); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to initialize host info for display:%" PRIu32, |
| __FUNCTION__, displayId); |
| display->unlock(); |
| return error; |
| } |
| display->unlock(); |
| } |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error HostComposer::createSecondaryDisplays( |
| Device* device, const AddDisplayToDeviceFunction& addDisplayToDeviceFn) { |
| HWC2::Error error = HWC2::Error::None; |
| |
| 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<std::string> 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<int> propIntParts; |
| for (const std::string& propStringPart : propStringParts) { |
| uint64_t propUintPart; |
| if (!android::base::ParseUint(propStringPart, &propUintPart)) { |
| ALOGE("%s: Invalid syntax for system prop %s which is %s", __FUNCTION__, |
| kExternalDisplayProp, propString.c_str()); |
| return HWC2::Error::BadParameter; |
| } |
| propIntParts.push_back(static_cast<int>(propUintPart)); |
| } |
| |
| static constexpr const uint32_t kHostDisplayIdStart = 6; |
| |
| uint32_t secondaryDisplayIndex = 1; |
| while (!propIntParts.empty()) { |
| int width = propIntParts[1]; |
| int height = propIntParts[2]; |
| int dpiX = propIntParts[3]; |
| int dpiY = propIntParts[3]; |
| int refreshRateHz = 160; |
| |
| propIntParts.erase(propIntParts.begin(), propIntParts.begin() + 5); |
| |
| uint32_t expectedHostDisplayId = |
| kHostDisplayIdStart + secondaryDisplayIndex - 1; |
| uint32_t actualHostDisplayId = 0; |
| |
| DEFINE_AND_VALIDATE_HOST_CONNECTION |
| hostCon->lock(); |
| rcEnc->rcDestroyDisplay(rcEnc, expectedHostDisplayId); |
| rcEnc->rcCreateDisplay(rcEnc, &actualHostDisplayId); |
| rcEnc->rcSetDisplayPose(rcEnc, actualHostDisplayId, -1, -1, width, height); |
| hostCon->unlock(); |
| |
| if (actualHostDisplayId != expectedHostDisplayId) { |
| ALOGE( |
| "Something wrong with host displayId allocation, expected %d " |
| "but received %d", |
| expectedHostDisplayId, actualHostDisplayId); |
| } |
| |
| auto display = |
| std::make_unique<Display>(*device, this, secondaryDisplayIndex++); |
| if (display == nullptr) { |
| ALOGE("%s failed to allocate display", __FUNCTION__); |
| return HWC2::Error::NoResources; |
| } |
| |
| auto displayId = display->getId(); |
| |
| error = display->init(width, height, dpiX, dpiY, refreshRateHz); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to initialize display:%" PRIu64, __FUNCTION__, |
| displayId); |
| return error; |
| } |
| |
| error = createHostComposerDisplayInfo(display.get(), actualHostDisplayId); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to initialize host info for display:%" PRIu64, |
| __FUNCTION__, displayId); |
| return error; |
| } |
| |
| error = addDisplayToDeviceFn(std::move(display)); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s failed to add display:%" PRIu64, __FUNCTION__, displayId); |
| return error; |
| } |
| } |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error HostComposer::createHostComposerDisplayInfo( |
| Display* display, uint32_t hostDisplayId) { |
| HWC2::Error error = HWC2::Error::None; |
| |
| hwc2_display_t displayId = display->getId(); |
| hwc2_config_t displayConfigId; |
| int32_t displayWidth; |
| int32_t displayHeight; |
| |
| 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); |
| } |
| |
| HostComposerDisplayInfo& displayInfo = mDisplayInfos[displayId]; |
| |
| displayInfo.hostDisplayId = hostDisplayId; |
| |
| displayInfo.compositionResultBuffer = |
| AllocateDisplayColorBuffer(displayWidth, displayHeight); |
| if (displayInfo.compositionResultBuffer == nullptr) { |
| ALOGE("%s: display:%" PRIu64 " failed to create target buffer", |
| __FUNCTION__, displayId); |
| return HWC2::Error::NoResources; |
| } |
| |
| if (mIsMinigbm) { |
| displayInfo.compositionResultDrmBuffer.reset( |
| new DrmBuffer(displayInfo.compositionResultBuffer, mDrmPresenter)); |
| |
| uint32_t vsyncPeriod = 1000 * 1000 * 1000 / mDrmPresenter.refreshRate(); |
| error = display->setVsyncPeriod(vsyncPeriod); |
| if (error != HWC2::Error::None) { |
| ALOGE("%s: display:%" PRIu64 " failed to set vsync height", __FUNCTION__, |
| displayId); |
| return error; |
| } |
| } |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error HostComposer::onDisplayDestroy(Display* display) { |
| hwc2_display_t 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; |
| } |
| |
| HostComposerDisplayInfo& displayInfo = mDisplayInfos[displayId]; |
| |
| if (displayId != 0) { |
| DEFINE_AND_VALIDATE_HOST_CONNECTION |
| hostCon->lock(); |
| rcEnc->rcDestroyDisplay(rcEnc, displayInfo.hostDisplayId); |
| hostCon->unlock(); |
| } |
| |
| FreeDisplayColorBuffer(displayInfo.compositionResultBuffer); |
| |
| mDisplayInfos.erase(it); |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error HostComposer::onDisplayClientTargetSet(Display* display) { |
| hwc2_display_t 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; |
| } |
| |
| HostComposerDisplayInfo& displayInfo = mDisplayInfos[displayId]; |
| |
| if (mIsMinigbm) { |
| FencedBuffer& clientTargetFencedBuffer = display->getClientTarget(); |
| |
| displayInfo.clientTargetDrmBuffer.reset( |
| new DrmBuffer(clientTargetFencedBuffer.getBuffer(), mDrmPresenter)); |
| } |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error HostComposer::validateDisplay( |
| Display* display, std::unordered_map<hwc2_layer_t, HWC2::Composition>* |
| layerCompositionChanges) { |
| DEFINE_AND_VALIDATE_HOST_CONNECTION |
| hostCon->lock(); |
| bool hostCompositionV1 = rcEnc->hasHostCompositionV1(); |
| bool hostCompositionV2 = rcEnc->hasHostCompositionV2(); |
| hostCon->unlock(); |
| |
| const std::vector<Layer*> layers = display->getOrderedLayers(); |
| |
| if (hostCompositionV1 || hostCompositionV2) { |
| // Support Device and SolidColor, otherwise, fallback all layers to Client. |
| bool fallBack = false; |
| // TODO: use local var compositiontype, avoid call getCompositionType() many |
| // times |
| for (auto& layer : layers) { |
| if (layer->getCompositionType() == HWC2::Composition::Invalid) { |
| // Log error for unused layers, layer leak? |
| ALOGE("%s layer %u CompositionType(%d) not set", __FUNCTION__, |
| (uint32_t)layer->getId(), layer->getCompositionType()); |
| continue; |
| } |
| if (layer->getCompositionType() == HWC2::Composition::Client || |
| layer->getCompositionType() == HWC2::Composition::Cursor || |
| layer->getCompositionType() == HWC2::Composition::Sideband) { |
| ALOGW("%s: layer %u CompositionType %d, fallback", __FUNCTION__, |
| (uint32_t)layer->getId(), layer->getCompositionType()); |
| fallBack = true; |
| break; |
| } |
| } |
| |
| if (display->hasColorTransform()) { |
| fallBack = true; |
| } |
| |
| if (fallBack) { |
| for (auto& layer : layers) { |
| if (layer->getCompositionType() == HWC2::Composition::Invalid) { |
| continue; |
| } |
| if (layer->getCompositionType() != HWC2::Composition::Client) { |
| (*layerCompositionChanges)[layer->getId()] = |
| HWC2::Composition::Client; |
| } |
| } |
| } |
| } else { |
| for (auto& layer : layers) { |
| if (layer->getCompositionType() != HWC2::Composition::Client) { |
| (*layerCompositionChanges)[layer->getId()] = HWC2::Composition::Client; |
| } |
| } |
| } |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error HostComposer::presentDisplay(Display* display, |
| int32_t* outRetireFence) { |
| auto it = mDisplayInfos.find(display->getId()); |
| if (it == mDisplayInfos.end()) { |
| ALOGE("%s: failed to find display buffers for display:%" PRIu64, |
| __FUNCTION__, display->getId()); |
| return HWC2::Error::BadDisplay; |
| } |
| |
| HostComposerDisplayInfo& displayInfo = it->second; |
| |
| DEFINE_AND_VALIDATE_HOST_CONNECTION |
| hostCon->lock(); |
| bool hostCompositionV1 = rcEnc->hasHostCompositionV1(); |
| bool hostCompositionV2 = rcEnc->hasHostCompositionV2(); |
| hostCon->unlock(); |
| |
| // Ff we supports v2, then discard v1 |
| if (hostCompositionV2) { |
| hostCompositionV1 = false; |
| } |
| |
| const std::vector<Layer*> layers = display->getOrderedLayers(); |
| if (hostCompositionV2 || hostCompositionV1) { |
| uint32_t numLayer = 0; |
| for (auto layer : layers) { |
| if (layer->getCompositionType() == HWC2::Composition::Device || |
| layer->getCompositionType() == HWC2::Composition::SolidColor) { |
| numLayer++; |
| } |
| } |
| |
| DEBUG_LOG("%s: presenting display:%" PRIu64 " with %d layers", __FUNCTION__, |
| display->getId(), static_cast<int>(layers.size())); |
| |
| display->clearReleaseFencesAndIdsLocked(); |
| |
| if (numLayer == 0) { |
| ALOGW( |
| "%s display has no layers to compose, flushing client target buffer.", |
| __FUNCTION__); |
| |
| FencedBuffer& displayClientTarget = display->getClientTarget(); |
| if (displayClientTarget.getBuffer() != nullptr) { |
| if (mIsMinigbm) { |
| int retireFence; |
| displayInfo.clientTargetDrmBuffer->flushToDisplay(display->getId(), |
| &retireFence); |
| *outRetireFence = dup(retireFence); |
| close(retireFence); |
| } else { |
| post(hostCon, rcEnc, displayClientTarget.getBuffer()); |
| *outRetireFence = displayClientTarget.getFence(); |
| } |
| } |
| return HWC2::Error::None; |
| } |
| |
| std::unique_ptr<ComposeMsg> composeMsg; |
| std::unique_ptr<ComposeMsg_v2> composeMsgV2; |
| |
| if (hostCompositionV1) { |
| composeMsg.reset(new ComposeMsg(numLayer)); |
| } else { |
| composeMsgV2.reset(new ComposeMsg_v2(numLayer)); |
| } |
| |
| // Handle the composition |
| ComposeDevice* p; |
| ComposeDevice_v2* p2; |
| ComposeLayer* l; |
| |
| if (hostCompositionV1) { |
| p = composeMsg->get(); |
| l = p->layer; |
| } else { |
| p2 = composeMsgV2->get(); |
| l = p2->layer; |
| } |
| |
| int releaseLayersCount = 0; |
| for (auto layer : layers) { |
| // TODO: use local var composisitonType to store getCompositionType() |
| if (layer->getCompositionType() != HWC2::Composition::Device && |
| layer->getCompositionType() != HWC2::Composition::SolidColor) { |
| ALOGE("%s: Unsupported composition types %d layer %u", __FUNCTION__, |
| layer->getCompositionType(), (uint32_t)layer->getId()); |
| continue; |
| } |
| // send layer composition command to host |
| if (layer->getCompositionType() == HWC2::Composition::Device) { |
| display->addReleaseLayerLocked(layer->getId()); |
| releaseLayersCount++; |
| |
| int fence = layer->getBuffer().getFence(); |
| if (fence != -1) { |
| int err = sync_wait(fence, 3000); |
| if (err < 0 && errno == ETIME) { |
| ALOGE("%s waited on fence %d for 3000 ms", __FUNCTION__, fence); |
| } |
| close(fence); |
| } else { |
| ALOGV("%s: acquire fence not set for layer %u", __FUNCTION__, |
| (uint32_t)layer->getId()); |
| } |
| const native_handle_t* cb = layer->getBuffer().getBuffer(); |
| if (cb != nullptr) { |
| l->cbHandle = hostCon->grallocHelper()->getHostHandle(cb); |
| } else { |
| ALOGE("%s null buffer for layer %d", __FUNCTION__, |
| (uint32_t)layer->getId()); |
| } |
| } else { |
| // solidcolor has no buffer |
| l->cbHandle = 0; |
| } |
| l->composeMode = (hwc2_composition_t)layer->getCompositionType(); |
| l->displayFrame = layer->getDisplayFrame(); |
| l->crop = layer->getSourceCrop(); |
| l->blendMode = static_cast<int32_t>(layer->getBlendMode()); |
| l->alpha = layer->getPlaneAlpha(); |
| l->color = layer->getColor(); |
| l->transform = layer->getTransform(); |
| ALOGV( |
| " cb %d blendmode %d alpha %f %d %d %d %d z %d" |
| " composeMode %d, transform %d", |
| l->cbHandle, l->blendMode, l->alpha, l->displayFrame.left, |
| l->displayFrame.top, l->displayFrame.right, l->displayFrame.bottom, |
| layer->getZ(), l->composeMode, l->transform); |
| l++; |
| } |
| if (hostCompositionV1) { |
| p->version = 1; |
| p->targetHandle = hostCon->grallocHelper()->getHostHandle( |
| displayInfo.compositionResultBuffer); |
| p->numLayers = numLayer; |
| } else { |
| p2->version = 2; |
| p2->displayId = displayInfo.hostDisplayId; |
| p2->targetHandle = hostCon->grallocHelper()->getHostHandle( |
| displayInfo.compositionResultBuffer); |
| p2->numLayers = numLayer; |
| } |
| |
| hostCon->lock(); |
| if (rcEnc->hasAsyncFrameCommands()) { |
| if (mIsMinigbm) { |
| if (hostCompositionV1) { |
| rcEnc->rcComposeAsyncWithoutPost( |
| rcEnc, sizeof(ComposeDevice) + numLayer * sizeof(ComposeLayer), |
| (void*)p); |
| } else { |
| rcEnc->rcComposeAsyncWithoutPost( |
| rcEnc, sizeof(ComposeDevice_v2) + numLayer * sizeof(ComposeLayer), |
| (void*)p2); |
| } |
| } else { |
| if (hostCompositionV1) { |
| rcEnc->rcComposeAsync( |
| rcEnc, sizeof(ComposeDevice) + numLayer * sizeof(ComposeLayer), |
| (void*)p); |
| } else { |
| rcEnc->rcComposeAsync( |
| rcEnc, sizeof(ComposeDevice_v2) + numLayer * sizeof(ComposeLayer), |
| (void*)p2); |
| } |
| } |
| } else { |
| if (mIsMinigbm) { |
| if (hostCompositionV1) { |
| rcEnc->rcComposeWithoutPost( |
| rcEnc, sizeof(ComposeDevice) + numLayer * sizeof(ComposeLayer), |
| (void*)p); |
| } else { |
| rcEnc->rcComposeWithoutPost( |
| rcEnc, sizeof(ComposeDevice_v2) + numLayer * sizeof(ComposeLayer), |
| (void*)p2); |
| } |
| } else { |
| if (hostCompositionV1) { |
| rcEnc->rcCompose( |
| rcEnc, sizeof(ComposeDevice) + numLayer * sizeof(ComposeLayer), |
| (void*)p); |
| } else { |
| rcEnc->rcCompose( |
| rcEnc, sizeof(ComposeDevice_v2) + numLayer * sizeof(ComposeLayer), |
| (void*)p2); |
| } |
| } |
| } |
| |
| hostCon->unlock(); |
| |
| // Send a retire fence and use it as the release fence for all layers, |
| // since media expects it |
| EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_ANDROID, |
| EGL_NO_NATIVE_FENCE_FD_ANDROID}; |
| |
| uint64_t sync_handle, thread_handle; |
| int retire_fd; |
| |
| // We don't use rc command to sync if we are using ANGLE on the guest with |
| // virtio-gpu. |
| bool useRcCommandToSync = !(mUseAngle && mIsMinigbm); |
| |
| if (useRcCommandToSync) { |
| hostCon->lock(); |
| rcEnc->rcCreateSyncKHR(rcEnc, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs, |
| 2 * sizeof(EGLint), true /* destroy when signaled */, |
| &sync_handle, &thread_handle); |
| hostCon->unlock(); |
| } |
| |
| if (mIsMinigbm) { |
| displayInfo.compositionResultDrmBuffer->flushToDisplay(display->getId(), |
| &retire_fd); |
| } else { |
| goldfish_sync_queue_work(mSyncDeviceFd, sync_handle, thread_handle, |
| &retire_fd); |
| } |
| |
| for (size_t i = 0; i < releaseLayersCount; ++i) { |
| display->addReleaseFenceLocked(dup(retire_fd)); |
| } |
| |
| *outRetireFence = dup(retire_fd); |
| close(retire_fd); |
| if (useRcCommandToSync) { |
| hostCon->lock(); |
| if (rcEnc->hasAsyncFrameCommands()) { |
| rcEnc->rcDestroySyncKHRAsync(rcEnc, sync_handle); |
| } else { |
| rcEnc->rcDestroySyncKHR(rcEnc, sync_handle); |
| } |
| hostCon->unlock(); |
| } |
| |
| } else { |
| // we set all layers Composition::Client, so do nothing. |
| if (mIsMinigbm) { |
| int retireFence; |
| displayInfo.clientTargetDrmBuffer->flushToDisplay(display->getId(), |
| &retireFence); |
| *outRetireFence = dup(retireFence); |
| close(retireFence); |
| } else { |
| FencedBuffer& displayClientTarget = display->getClientTarget(); |
| post(hostCon, rcEnc, displayClientTarget.getBuffer()); |
| *outRetireFence = displayClientTarget.getFence(); |
| } |
| ALOGV("%s fallback to post, returns outRetireFence %d", __FUNCTION__, |
| *outRetireFence); |
| } |
| |
| return HWC2::Error::None; |
| } |
| |
| void HostComposer::post(HostConnection* hostCon, |
| ExtendedRCEncoderContext* rcEnc, buffer_handle_t h) { |
| assert(cb && "native_handle_t::from(h) failed"); |
| |
| hostCon->lock(); |
| rcEnc->rcFBPost(rcEnc, hostCon->grallocHelper()->getHostHandle(h)); |
| hostCon->flush(); |
| hostCon->unlock(); |
| } |
| |
| } // namespace android |