| /* |
| * Copyright (C) 2010 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. |
| */ |
| |
| // #define LOG_NDEBUG 0 |
| |
| #undef LOG_TAG |
| #define LOG_TAG "HWComposer" |
| #define ATRACE_TAG ATRACE_TAG_GRAPHICS |
| |
| #include <inttypes.h> |
| #include <math.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| |
| #include <utils/Errors.h> |
| #include <utils/misc.h> |
| #include <utils/NativeHandle.h> |
| #include <utils/String8.h> |
| #include <utils/Thread.h> |
| #include <utils/Trace.h> |
| #include <utils/Vector.h> |
| |
| #include <ui/GraphicBuffer.h> |
| |
| #include <hardware/hardware.h> |
| #include <hardware/hwcomposer.h> |
| |
| #include <android/configuration.h> |
| |
| #include <cutils/properties.h> |
| #include <log/log.h> |
| |
| #include "HWComposer.h" |
| #include "hwc2on1adapter/HWC2On1Adapter.h" |
| #include "HWC2.h" |
| #include "ComposerHal.h" |
| |
| #include "../Layer.h" // needed only for debugging |
| #include "../SurfaceFlinger.h" |
| |
| namespace android { |
| |
| #define MIN_HWC_HEADER_VERSION HWC_HEADER_VERSION |
| |
| // --------------------------------------------------------------------------- |
| |
| HWComposer::HWComposer(bool useVrComposer) |
| : mAdapter(), |
| mHwcDevice(), |
| mDisplayData(2), |
| mFreeDisplaySlots(), |
| mHwcDisplaySlots(), |
| mCBContext(), |
| mEventHandler(nullptr), |
| mVSyncCounts(), |
| mRemainingHwcVirtualDisplays(0), |
| mDumpMayLockUp(false) |
| { |
| for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) { |
| mLastHwVSync[i] = 0; |
| mVSyncCounts[i] = 0; |
| } |
| |
| loadHwcModule(useVrComposer); |
| } |
| |
| HWComposer::~HWComposer() {} |
| |
| void HWComposer::setEventHandler(EventHandler* handler) |
| { |
| if (handler == nullptr) { |
| ALOGE("setEventHandler: Rejected attempt to clear handler"); |
| return; |
| } |
| |
| bool wasNull = (mEventHandler == nullptr); |
| mEventHandler = handler; |
| |
| if (wasNull) { |
| auto hotplugHook = std::bind(&HWComposer::hotplug, this, |
| std::placeholders::_1, std::placeholders::_2); |
| mHwcDevice->registerHotplugCallback(hotplugHook); |
| auto invalidateHook = std::bind(&HWComposer::invalidate, this, |
| std::placeholders::_1); |
| mHwcDevice->registerRefreshCallback(invalidateHook); |
| auto vsyncHook = std::bind(&HWComposer::vsync, this, |
| std::placeholders::_1, std::placeholders::_2); |
| mHwcDevice->registerVsyncCallback(vsyncHook); |
| } |
| } |
| |
| // Load and prepare the hardware composer module. Sets mHwc. |
| void HWComposer::loadHwcModule(bool useVrComposer) |
| { |
| ALOGV("loadHwcModule"); |
| |
| #ifdef BYPASS_IHWC |
| (void)useVrComposer; // Silence unused parameter warning. |
| |
| hw_module_t const* module; |
| |
| if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) { |
| ALOGE("%s module not found, aborting", HWC_HARDWARE_MODULE_ID); |
| abort(); |
| } |
| |
| hw_device_t* device = nullptr; |
| int error = module->methods->open(module, HWC_HARDWARE_COMPOSER, &device); |
| if (error != 0) { |
| ALOGE("Failed to open HWC device (%s), aborting", strerror(-error)); |
| abort(); |
| } |
| |
| uint32_t majorVersion = (device->version >> 24) & 0xF; |
| if (majorVersion == 2) { |
| mHwcDevice = std::make_unique<HWC2::Device>( |
| reinterpret_cast<hwc2_device_t*>(device)); |
| } else { |
| mAdapter = std::make_unique<HWC2On1Adapter>( |
| reinterpret_cast<hwc_composer_device_1_t*>(device)); |
| uint8_t minorVersion = mAdapter->getHwc1MinorVersion(); |
| if (minorVersion < 1) { |
| ALOGE("Cannot adapt to HWC version %d.%d", |
| static_cast<int32_t>((minorVersion >> 8) & 0xF), |
| static_cast<int32_t>(minorVersion & 0xF)); |
| abort(); |
| } |
| mHwcDevice = std::make_unique<HWC2::Device>( |
| static_cast<hwc2_device_t*>(mAdapter.get())); |
| } |
| #else |
| mHwcDevice = std::make_unique<HWC2::Device>(useVrComposer); |
| #endif |
| |
| mRemainingHwcVirtualDisplays = mHwcDevice->getMaxVirtualDisplayCount(); |
| } |
| |
| bool HWComposer::hasCapability(HWC2::Capability capability) const |
| { |
| return mHwcDevice->getCapabilities().count(capability) > 0; |
| } |
| |
| bool HWComposer::isValidDisplay(int32_t displayId) const { |
| return static_cast<size_t>(displayId) < mDisplayData.size() && |
| mDisplayData[displayId].hwcDisplay; |
| } |
| |
| void HWComposer::validateChange(HWC2::Composition from, HWC2::Composition to) { |
| bool valid = true; |
| switch (from) { |
| case HWC2::Composition::Client: |
| valid = false; |
| break; |
| case HWC2::Composition::Device: |
| case HWC2::Composition::SolidColor: |
| valid = (to == HWC2::Composition::Client); |
| break; |
| case HWC2::Composition::Cursor: |
| case HWC2::Composition::Sideband: |
| valid = (to == HWC2::Composition::Client || |
| to == HWC2::Composition::Device); |
| break; |
| default: |
| break; |
| } |
| |
| if (!valid) { |
| ALOGE("Invalid layer type change: %s --> %s", to_string(from).c_str(), |
| to_string(to).c_str()); |
| } |
| } |
| |
| void HWComposer::hotplug(const std::shared_ptr<HWC2::Display>& display, |
| HWC2::Connection connected) { |
| ALOGV("hotplug: %" PRIu64 ", %s", display->getId(), |
| to_string(connected).c_str()); |
| int32_t disp = 0; |
| if (!mDisplayData[0].hwcDisplay) { |
| ALOGE_IF(connected != HWC2::Connection::Connected, "Assumed primary" |
| " display would be connected"); |
| mDisplayData[0].hwcDisplay = display; |
| mHwcDisplaySlots[display->getId()] = 0; |
| disp = DisplayDevice::DISPLAY_PRIMARY; |
| } else { |
| // Disconnect is handled through HWComposer::disconnectDisplay via |
| // SurfaceFlinger's onHotplugReceived callback handling |
| if (connected == HWC2::Connection::Connected) { |
| mDisplayData[1].hwcDisplay = display; |
| mHwcDisplaySlots[display->getId()] = 1; |
| } |
| disp = DisplayDevice::DISPLAY_EXTERNAL; |
| } |
| mEventHandler->onHotplugReceived(this, disp, |
| connected == HWC2::Connection::Connected); |
| } |
| |
| void HWComposer::invalidate(const std::shared_ptr<HWC2::Display>& /*display*/) { |
| mEventHandler->onInvalidateReceived(this); |
| } |
| |
| void HWComposer::vsync(const std::shared_ptr<HWC2::Display>& display, |
| int64_t timestamp) { |
| auto displayType = HWC2::DisplayType::Invalid; |
| auto error = display->getType(&displayType); |
| if (error != HWC2::Error::None) { |
| ALOGE("vsync: Failed to determine type of display %" PRIu64, |
| display->getId()); |
| return; |
| } |
| |
| if (displayType == HWC2::DisplayType::Virtual) { |
| ALOGE("Virtual display %" PRIu64 " passed to vsync callback", |
| display->getId()); |
| return; |
| } |
| |
| if (mHwcDisplaySlots.count(display->getId()) == 0) { |
| ALOGE("Unknown physical display %" PRIu64 " passed to vsync callback", |
| display->getId()); |
| return; |
| } |
| |
| int32_t disp = mHwcDisplaySlots[display->getId()]; |
| { |
| Mutex::Autolock _l(mLock); |
| |
| // There have been reports of HWCs that signal several vsync events |
| // with the same timestamp when turning the display off and on. This |
| // is a bug in the HWC implementation, but filter the extra events |
| // out here so they don't cause havoc downstream. |
| if (timestamp == mLastHwVSync[disp]) { |
| ALOGW("Ignoring duplicate VSYNC event from HWC (t=%" PRId64 ")", |
| timestamp); |
| return; |
| } |
| |
| mLastHwVSync[disp] = timestamp; |
| } |
| |
| char tag[16]; |
| snprintf(tag, sizeof(tag), "HW_VSYNC_%1u", disp); |
| ATRACE_INT(tag, ++mVSyncCounts[disp] & 1); |
| |
| mEventHandler->onVSyncReceived(this, disp, timestamp); |
| } |
| |
| status_t HWComposer::allocateVirtualDisplay(uint32_t width, uint32_t height, |
| android_pixel_format_t* format, int32_t *outId) { |
| if (mRemainingHwcVirtualDisplays == 0) { |
| ALOGE("allocateVirtualDisplay: No remaining virtual displays"); |
| return NO_MEMORY; |
| } |
| |
| if (SurfaceFlinger::maxVirtualDisplaySize != 0 && |
| (width > SurfaceFlinger::maxVirtualDisplaySize || |
| height > SurfaceFlinger::maxVirtualDisplaySize)) { |
| ALOGE("createVirtualDisplay: Can't create a virtual display with" |
| " a dimension > %" PRIu64 " (tried %u x %u)", |
| SurfaceFlinger::maxVirtualDisplaySize, width, height); |
| return INVALID_OPERATION; |
| } |
| |
| std::shared_ptr<HWC2::Display> display; |
| auto error = mHwcDevice->createVirtualDisplay(width, height, format, |
| &display); |
| if (error != HWC2::Error::None) { |
| ALOGE("allocateVirtualDisplay: Failed to create HWC virtual display"); |
| return NO_MEMORY; |
| } |
| |
| size_t displaySlot = 0; |
| if (!mFreeDisplaySlots.empty()) { |
| displaySlot = *mFreeDisplaySlots.begin(); |
| mFreeDisplaySlots.erase(displaySlot); |
| } else if (mDisplayData.size() < INT32_MAX) { |
| // Don't bother allocating a slot larger than we can return |
| displaySlot = mDisplayData.size(); |
| mDisplayData.resize(displaySlot + 1); |
| } else { |
| ALOGE("allocateVirtualDisplay: Unable to allocate a display slot"); |
| return NO_MEMORY; |
| } |
| |
| mDisplayData[displaySlot].hwcDisplay = display; |
| |
| --mRemainingHwcVirtualDisplays; |
| *outId = static_cast<int32_t>(displaySlot); |
| |
| return NO_ERROR; |
| } |
| |
| std::shared_ptr<HWC2::Layer> HWComposer::createLayer(int32_t displayId) { |
| if (!isValidDisplay(displayId)) { |
| ALOGE("Failed to create layer on invalid display %d", displayId); |
| return nullptr; |
| } |
| auto display = mDisplayData[displayId].hwcDisplay; |
| std::shared_ptr<HWC2::Layer> layer; |
| auto error = display->createLayer(&layer); |
| if (error != HWC2::Error::None) { |
| ALOGE("Failed to create layer on display %d: %s (%d)", displayId, |
| to_string(error).c_str(), static_cast<int32_t>(error)); |
| return nullptr; |
| } |
| return layer; |
| } |
| |
| nsecs_t HWComposer::getRefreshTimestamp(int32_t displayId) const { |
| // this returns the last refresh timestamp. |
| // if the last one is not available, we estimate it based on |
| // the refresh period and whatever closest timestamp we have. |
| Mutex::Autolock _l(mLock); |
| nsecs_t now = systemTime(CLOCK_MONOTONIC); |
| auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod(); |
| return now - ((now - mLastHwVSync[displayId]) % vsyncPeriod); |
| } |
| |
| bool HWComposer::isConnected(int32_t displayId) const { |
| if (!isValidDisplay(displayId)) { |
| ALOGE("isConnected: Attempted to access invalid display %d", displayId); |
| return false; |
| } |
| return mDisplayData[displayId].hwcDisplay->isConnected(); |
| } |
| |
| std::vector<std::shared_ptr<const HWC2::Display::Config>> |
| HWComposer::getConfigs(int32_t displayId) const { |
| if (!isValidDisplay(displayId)) { |
| ALOGE("getConfigs: Attempted to access invalid display %d", displayId); |
| return {}; |
| } |
| auto& displayData = mDisplayData[displayId]; |
| auto configs = mDisplayData[displayId].hwcDisplay->getConfigs(); |
| if (displayData.configMap.empty()) { |
| for (size_t i = 0; i < configs.size(); ++i) { |
| displayData.configMap[i] = configs[i]; |
| } |
| } |
| return configs; |
| } |
| |
| std::shared_ptr<const HWC2::Display::Config> |
| HWComposer::getActiveConfig(int32_t displayId) const { |
| if (!isValidDisplay(displayId)) { |
| ALOGV("getActiveConfigs: Attempted to access invalid display %d", |
| displayId); |
| return nullptr; |
| } |
| std::shared_ptr<const HWC2::Display::Config> config; |
| auto error = mDisplayData[displayId].hwcDisplay->getActiveConfig(&config); |
| if (error == HWC2::Error::BadConfig) { |
| ALOGE("getActiveConfig: No config active, returning null"); |
| return nullptr; |
| } else if (error != HWC2::Error::None) { |
| ALOGE("getActiveConfig failed for display %d: %s (%d)", displayId, |
| to_string(error).c_str(), static_cast<int32_t>(error)); |
| return nullptr; |
| } else if (!config) { |
| ALOGE("getActiveConfig returned an unknown config for display %d", |
| displayId); |
| return nullptr; |
| } |
| |
| return config; |
| } |
| |
| std::vector<android_color_mode_t> HWComposer::getColorModes(int32_t displayId) const { |
| std::vector<android_color_mode_t> modes; |
| |
| if (!isValidDisplay(displayId)) { |
| ALOGE("getColorModes: Attempted to access invalid display %d", |
| displayId); |
| return modes; |
| } |
| const std::shared_ptr<HWC2::Display>& hwcDisplay = |
| mDisplayData[displayId].hwcDisplay; |
| |
| auto error = hwcDisplay->getColorModes(&modes); |
| if (error != HWC2::Error::None) { |
| ALOGE("getColorModes failed for display %d: %s (%d)", displayId, |
| to_string(error).c_str(), static_cast<int32_t>(error)); |
| return std::vector<android_color_mode_t>(); |
| } |
| |
| return modes; |
| } |
| |
| status_t HWComposer::setActiveColorMode(int32_t displayId, android_color_mode_t mode) { |
| if (!isValidDisplay(displayId)) { |
| ALOGE("setActiveColorMode: Display %d is not valid", displayId); |
| return BAD_INDEX; |
| } |
| |
| auto& displayData = mDisplayData[displayId]; |
| auto error = displayData.hwcDisplay->setColorMode(mode); |
| if (error != HWC2::Error::None) { |
| ALOGE("setActiveConfig: Failed to set color mode %d on display %d: " |
| "%s (%d)", mode, displayId, to_string(error).c_str(), |
| static_cast<int32_t>(error)); |
| return UNKNOWN_ERROR; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| |
| void HWComposer::setVsyncEnabled(int32_t displayId, HWC2::Vsync enabled) { |
| if (displayId < 0 || displayId >= HWC_DISPLAY_VIRTUAL) { |
| ALOGD("setVsyncEnabled: Ignoring for virtual display %d", displayId); |
| return; |
| } |
| |
| if (!isValidDisplay(displayId)) { |
| ALOGE("setVsyncEnabled: Attempted to access invalid display %d", |
| displayId); |
| return; |
| } |
| |
| // NOTE: we use our own internal lock here because we have to call |
| // into the HWC with the lock held, and we want to make sure |
| // that even if HWC blocks (which it shouldn't), it won't |
| // affect other threads. |
| Mutex::Autolock _l(mVsyncLock); |
| auto& displayData = mDisplayData[displayId]; |
| if (enabled != displayData.vsyncEnabled) { |
| ATRACE_CALL(); |
| auto error = displayData.hwcDisplay->setVsyncEnabled(enabled); |
| if (error == HWC2::Error::None) { |
| displayData.vsyncEnabled = enabled; |
| |
| char tag[16]; |
| snprintf(tag, sizeof(tag), "HW_VSYNC_ON_%1u", displayId); |
| ATRACE_INT(tag, enabled == HWC2::Vsync::Enable ? 1 : 0); |
| } else { |
| ALOGE("setVsyncEnabled: Failed to set vsync to %s on %d/%" PRIu64 |
| ": %s (%d)", to_string(enabled).c_str(), displayId, |
| mDisplayData[displayId].hwcDisplay->getId(), |
| to_string(error).c_str(), static_cast<int32_t>(error)); |
| } |
| } |
| } |
| |
| status_t HWComposer::setClientTarget(int32_t displayId, uint32_t slot, |
| const sp<Fence>& acquireFence, const sp<GraphicBuffer>& target, |
| android_dataspace_t dataspace) { |
| if (!isValidDisplay(displayId)) { |
| return BAD_INDEX; |
| } |
| |
| ALOGV("setClientTarget for display %d", displayId); |
| auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; |
| auto error = hwcDisplay->setClientTarget(slot, target, acquireFence, dataspace); |
| if (error != HWC2::Error::None) { |
| ALOGE("Failed to set client target for display %d: %s (%d)", displayId, |
| to_string(error).c_str(), static_cast<int32_t>(error)); |
| return BAD_VALUE; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| status_t HWComposer::prepare(DisplayDevice& displayDevice) { |
| ATRACE_CALL(); |
| |
| Mutex::Autolock _l(mDisplayLock); |
| auto displayId = displayDevice.getHwcDisplayId(); |
| if (displayId == DisplayDevice::DISPLAY_ID_INVALID) { |
| ALOGV("Skipping HWComposer prepare for non-HWC display"); |
| return NO_ERROR; |
| } |
| if (!isValidDisplay(displayId)) { |
| return BAD_INDEX; |
| } |
| |
| auto& displayData = mDisplayData[displayId]; |
| auto& hwcDisplay = displayData.hwcDisplay; |
| if (!hwcDisplay->isConnected()) { |
| return NO_ERROR; |
| } |
| |
| mDumpMayLockUp = true; |
| |
| uint32_t numTypes = 0; |
| uint32_t numRequests = 0; |
| auto error = hwcDisplay->validate(&numTypes, &numRequests); |
| if (error != HWC2::Error::None && error != HWC2::Error::HasChanges) { |
| ALOGE("prepare: validate failed for display %d: %s (%d)", displayId, |
| to_string(error).c_str(), static_cast<int32_t>(error)); |
| return BAD_INDEX; |
| } |
| |
| std::unordered_map<std::shared_ptr<HWC2::Layer>, HWC2::Composition> |
| changedTypes; |
| changedTypes.reserve(numTypes); |
| error = hwcDisplay->getChangedCompositionTypes(&changedTypes); |
| if (error != HWC2::Error::None) { |
| ALOGE("prepare: getChangedCompositionTypes failed on display %d: " |
| "%s (%d)", displayId, to_string(error).c_str(), |
| static_cast<int32_t>(error)); |
| return BAD_INDEX; |
| } |
| |
| |
| displayData.displayRequests = static_cast<HWC2::DisplayRequest>(0); |
| std::unordered_map<std::shared_ptr<HWC2::Layer>, HWC2::LayerRequest> |
| layerRequests; |
| layerRequests.reserve(numRequests); |
| error = hwcDisplay->getRequests(&displayData.displayRequests, |
| &layerRequests); |
| if (error != HWC2::Error::None) { |
| ALOGE("prepare: getRequests failed on display %d: %s (%d)", displayId, |
| to_string(error).c_str(), static_cast<int32_t>(error)); |
| return BAD_INDEX; |
| } |
| |
| displayData.hasClientComposition = false; |
| displayData.hasDeviceComposition = false; |
| for (auto& layer : displayDevice.getVisibleLayersSortedByZ()) { |
| auto hwcLayer = layer->getHwcLayer(displayId); |
| |
| if (changedTypes.count(hwcLayer) != 0) { |
| // We pass false so we only update our state and don't call back |
| // into the HWC device |
| validateChange(layer->getCompositionType(displayId), |
| changedTypes[hwcLayer]); |
| layer->setCompositionType(displayId, changedTypes[hwcLayer], false); |
| } |
| |
| switch (layer->getCompositionType(displayId)) { |
| case HWC2::Composition::Client: |
| displayData.hasClientComposition = true; |
| break; |
| case HWC2::Composition::Device: |
| case HWC2::Composition::SolidColor: |
| case HWC2::Composition::Cursor: |
| case HWC2::Composition::Sideband: |
| displayData.hasDeviceComposition = true; |
| break; |
| default: |
| break; |
| } |
| |
| if (layerRequests.count(hwcLayer) != 0 && |
| layerRequests[hwcLayer] == |
| HWC2::LayerRequest::ClearClientTarget) { |
| layer->setClearClientTarget(displayId, true); |
| } else { |
| if (layerRequests.count(hwcLayer) != 0) { |
| ALOGE("prepare: Unknown layer request: %s", |
| to_string(layerRequests[hwcLayer]).c_str()); |
| } |
| layer->setClearClientTarget(displayId, false); |
| } |
| } |
| |
| error = hwcDisplay->acceptChanges(); |
| if (error != HWC2::Error::None) { |
| ALOGE("prepare: acceptChanges failed: %s", to_string(error).c_str()); |
| return BAD_INDEX; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| bool HWComposer::hasDeviceComposition(int32_t displayId) const { |
| if (displayId == DisplayDevice::DISPLAY_ID_INVALID) { |
| // Displays without a corresponding HWC display are never composed by |
| // the device |
| return false; |
| } |
| if (!isValidDisplay(displayId)) { |
| ALOGE("hasDeviceComposition: Invalid display %d", displayId); |
| return false; |
| } |
| return mDisplayData[displayId].hasDeviceComposition; |
| } |
| |
| bool HWComposer::hasClientComposition(int32_t displayId) const { |
| if (displayId == DisplayDevice::DISPLAY_ID_INVALID) { |
| // Displays without a corresponding HWC display are always composed by |
| // the client |
| return true; |
| } |
| if (!isValidDisplay(displayId)) { |
| ALOGE("hasClientComposition: Invalid display %d", displayId); |
| return true; |
| } |
| return mDisplayData[displayId].hasClientComposition; |
| } |
| |
| sp<Fence> HWComposer::getPresentFence(int32_t displayId) const { |
| if (!isValidDisplay(displayId)) { |
| ALOGE("getPresentFence failed for invalid display %d", displayId); |
| return Fence::NO_FENCE; |
| } |
| return mDisplayData[displayId].lastPresentFence; |
| } |
| |
| sp<Fence> HWComposer::getLayerReleaseFence(int32_t displayId, |
| const std::shared_ptr<HWC2::Layer>& layer) const { |
| if (!isValidDisplay(displayId)) { |
| ALOGE("getLayerReleaseFence: Invalid display"); |
| return Fence::NO_FENCE; |
| } |
| auto displayFences = mDisplayData[displayId].releaseFences; |
| if (displayFences.count(layer) == 0) { |
| ALOGV("getLayerReleaseFence: Release fence not found"); |
| return Fence::NO_FENCE; |
| } |
| return displayFences[layer]; |
| } |
| |
| status_t HWComposer::presentAndGetReleaseFences(int32_t displayId) { |
| ATRACE_CALL(); |
| |
| if (!isValidDisplay(displayId)) { |
| return BAD_INDEX; |
| } |
| |
| auto& displayData = mDisplayData[displayId]; |
| auto& hwcDisplay = displayData.hwcDisplay; |
| auto error = hwcDisplay->present(&displayData.lastPresentFence); |
| |
| mDumpMayLockUp = false; |
| |
| if (error != HWC2::Error::None) { |
| ALOGE("presentAndGetReleaseFences: failed for display %d: %s (%d)", |
| displayId, to_string(error).c_str(), static_cast<int32_t>(error)); |
| return UNKNOWN_ERROR; |
| } |
| |
| std::unordered_map<std::shared_ptr<HWC2::Layer>, sp<Fence>> releaseFences; |
| error = hwcDisplay->getReleaseFences(&releaseFences); |
| if (error != HWC2::Error::None) { |
| ALOGE("presentAndGetReleaseFences: Failed to get release fences " |
| "for display %d: %s (%d)", |
| displayId, to_string(error).c_str(), |
| static_cast<int32_t>(error)); |
| return UNKNOWN_ERROR; |
| } |
| |
| displayData.releaseFences = std::move(releaseFences); |
| |
| return NO_ERROR; |
| } |
| |
| status_t HWComposer::setPowerMode(int32_t displayId, int32_t intMode) { |
| ALOGV("setPowerMode(%d, %d)", displayId, intMode); |
| if (!isValidDisplay(displayId)) { |
| ALOGE("setPowerMode: Bad display"); |
| return BAD_INDEX; |
| } |
| if (displayId >= VIRTUAL_DISPLAY_ID_BASE) { |
| ALOGE("setPowerMode: Virtual display %d passed in, returning", |
| displayId); |
| return BAD_INDEX; |
| } |
| |
| auto mode = static_cast<HWC2::PowerMode>(intMode); |
| if (mode == HWC2::PowerMode::Off) { |
| setVsyncEnabled(displayId, HWC2::Vsync::Disable); |
| } |
| |
| auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; |
| switch (mode) { |
| case HWC2::PowerMode::Off: |
| case HWC2::PowerMode::On: |
| ALOGV("setPowerMode: Calling HWC %s", to_string(mode).c_str()); |
| { |
| auto error = hwcDisplay->setPowerMode(mode); |
| if (error != HWC2::Error::None) { |
| ALOGE("setPowerMode: Unable to set power mode %s for " |
| "display %d: %s (%d)", to_string(mode).c_str(), |
| displayId, to_string(error).c_str(), |
| static_cast<int32_t>(error)); |
| } |
| } |
| break; |
| case HWC2::PowerMode::Doze: |
| case HWC2::PowerMode::DozeSuspend: |
| ALOGV("setPowerMode: Calling HWC %s", to_string(mode).c_str()); |
| { |
| bool supportsDoze = false; |
| auto error = hwcDisplay->supportsDoze(&supportsDoze); |
| if (error != HWC2::Error::None) { |
| ALOGE("setPowerMode: Unable to query doze support for " |
| "display %d: %s (%d)", displayId, |
| to_string(error).c_str(), |
| static_cast<int32_t>(error)); |
| } |
| if (!supportsDoze) { |
| mode = HWC2::PowerMode::On; |
| } |
| |
| error = hwcDisplay->setPowerMode(mode); |
| if (error != HWC2::Error::None) { |
| ALOGE("setPowerMode: Unable to set power mode %s for " |
| "display %d: %s (%d)", to_string(mode).c_str(), |
| displayId, to_string(error).c_str(), |
| static_cast<int32_t>(error)); |
| } |
| } |
| break; |
| default: |
| ALOGV("setPowerMode: Not calling HWC"); |
| break; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| status_t HWComposer::setActiveConfig(int32_t displayId, size_t configId) { |
| if (!isValidDisplay(displayId)) { |
| ALOGE("setActiveConfig: Display %d is not valid", displayId); |
| return BAD_INDEX; |
| } |
| |
| auto& displayData = mDisplayData[displayId]; |
| if (displayData.configMap.count(configId) == 0) { |
| ALOGE("setActiveConfig: Invalid config %zd", configId); |
| return BAD_INDEX; |
| } |
| |
| auto error = displayData.hwcDisplay->setActiveConfig( |
| displayData.configMap[configId]); |
| if (error != HWC2::Error::None) { |
| ALOGE("setActiveConfig: Failed to set config %zu on display %d: " |
| "%s (%d)", configId, displayId, to_string(error).c_str(), |
| static_cast<int32_t>(error)); |
| return UNKNOWN_ERROR; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| status_t HWComposer::setColorTransform(int32_t displayId, |
| const mat4& transform) { |
| if (!isValidDisplay(displayId)) { |
| ALOGE("setColorTransform: Display %d is not valid", displayId); |
| return BAD_INDEX; |
| } |
| |
| auto& displayData = mDisplayData[displayId]; |
| bool isIdentity = transform == mat4(); |
| auto error = displayData.hwcDisplay->setColorTransform(transform, |
| isIdentity ? HAL_COLOR_TRANSFORM_IDENTITY : |
| HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX); |
| if (error != HWC2::Error::None) { |
| ALOGE("setColorTransform: Failed to set transform on display %d: " |
| "%s (%d)", displayId, to_string(error).c_str(), |
| static_cast<int32_t>(error)); |
| return UNKNOWN_ERROR; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| void HWComposer::disconnectDisplay(int displayId) { |
| LOG_ALWAYS_FATAL_IF(displayId < 0); |
| auto& displayData = mDisplayData[displayId]; |
| |
| auto displayType = HWC2::DisplayType::Invalid; |
| auto error = displayData.hwcDisplay->getType(&displayType); |
| if (error != HWC2::Error::None) { |
| ALOGE("disconnectDisplay: Failed to determine type of display %d", |
| displayId); |
| return; |
| } |
| |
| // If this was a virtual display, add its slot back for reuse by future |
| // virtual displays |
| if (displayType == HWC2::DisplayType::Virtual) { |
| mFreeDisplaySlots.insert(displayId); |
| ++mRemainingHwcVirtualDisplays; |
| } |
| |
| auto hwcId = displayData.hwcDisplay->getId(); |
| mHwcDisplaySlots.erase(hwcId); |
| displayData.reset(); |
| } |
| |
| status_t HWComposer::setOutputBuffer(int32_t displayId, |
| const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) { |
| if (!isValidDisplay(displayId)) { |
| ALOGE("setOutputBuffer: Display %d is not valid", displayId); |
| return BAD_INDEX; |
| } |
| |
| auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; |
| auto displayType = HWC2::DisplayType::Invalid; |
| auto error = hwcDisplay->getType(&displayType); |
| if (error != HWC2::Error::None) { |
| ALOGE("setOutputBuffer: Failed to determine type of display %d", |
| displayId); |
| return NAME_NOT_FOUND; |
| } |
| |
| if (displayType != HWC2::DisplayType::Virtual) { |
| ALOGE("setOutputBuffer: Display %d is not virtual", displayId); |
| return INVALID_OPERATION; |
| } |
| |
| error = hwcDisplay->setOutputBuffer(buffer, acquireFence); |
| if (error != HWC2::Error::None) { |
| ALOGE("setOutputBuffer: Failed to set buffer on display %d: %s (%d)", |
| displayId, to_string(error).c_str(), |
| static_cast<int32_t>(error)); |
| return UNKNOWN_ERROR; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| void HWComposer::clearReleaseFences(int32_t displayId) { |
| if (!isValidDisplay(displayId)) { |
| ALOGE("clearReleaseFences: Display %d is not valid", displayId); |
| return; |
| } |
| mDisplayData[displayId].releaseFences.clear(); |
| } |
| |
| std::unique_ptr<HdrCapabilities> HWComposer::getHdrCapabilities( |
| int32_t displayId) { |
| if (!isValidDisplay(displayId)) { |
| ALOGE("getHdrCapabilities: Display %d is not valid", displayId); |
| return nullptr; |
| } |
| |
| auto& hwcDisplay = mDisplayData[displayId].hwcDisplay; |
| std::unique_ptr<HdrCapabilities> capabilities; |
| auto error = hwcDisplay->getHdrCapabilities(&capabilities); |
| if (error != HWC2::Error::None) { |
| ALOGE("getOutputCapabilities: Failed to get capabilities on display %d:" |
| " %s (%d)", displayId, to_string(error).c_str(), |
| static_cast<int32_t>(error)); |
| return nullptr; |
| } |
| |
| return capabilities; |
| } |
| |
| // Converts a PixelFormat to a human-readable string. Max 11 chars. |
| // (Could use a table of prefab String8 objects.) |
| /* |
| static String8 getFormatStr(PixelFormat format) { |
| switch (format) { |
| case PIXEL_FORMAT_RGBA_8888: return String8("RGBA_8888"); |
| case PIXEL_FORMAT_RGBX_8888: return String8("RGBx_8888"); |
| case PIXEL_FORMAT_RGB_888: return String8("RGB_888"); |
| case PIXEL_FORMAT_RGB_565: return String8("RGB_565"); |
| case PIXEL_FORMAT_BGRA_8888: return String8("BGRA_8888"); |
| case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: |
| return String8("ImplDef"); |
| default: |
| String8 result; |
| result.appendFormat("? %08x", format); |
| return result; |
| } |
| } |
| */ |
| |
| bool HWComposer::isUsingVrComposer() const { |
| #ifdef BYPASS_IHWC |
| return false; |
| #else |
| return getComposer()->isUsingVrComposer(); |
| #endif |
| } |
| |
| void HWComposer::dump(String8& result) const { |
| if (mDumpMayLockUp) { |
| result.append("HWComposer dump skipped because present in progress"); |
| return; |
| } |
| |
| // TODO: In order to provide a dump equivalent to HWC1, we need to shadow |
| // all the state going into the layers. This is probably better done in |
| // Layer itself, but it's going to take a bit of work to get there. |
| result.append(mHwcDevice->dump().c_str()); |
| } |
| |
| // --------------------------------------------------------------------------- |
| |
| HWComposer::DisplayData::DisplayData() |
| : hasClientComposition(false), |
| hasDeviceComposition(false), |
| hwcDisplay(), |
| lastPresentFence(Fence::NO_FENCE), |
| outbufHandle(nullptr), |
| outbufAcquireFence(Fence::NO_FENCE), |
| vsyncEnabled(HWC2::Vsync::Disable) { |
| ALOGV("Created new DisplayData"); |
| } |
| |
| HWComposer::DisplayData::~DisplayData() { |
| } |
| |
| void HWComposer::DisplayData::reset() { |
| ALOGV("DisplayData reset"); |
| *this = DisplayData(); |
| } |
| |
| // --------------------------------------------------------------------------- |
| }; // namespace android |