/* * 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 "Device.h" #include #include "GuestComposer.h" #include "HostComposer.h" namespace android { namespace { template static hwc2_function_pointer_t asFP(T function) { static_assert(std::is_same::value, "Incompatible function pointer"); return reinterpret_cast(function); } static int CloseHook(hw_device_t* dev) { Device* device = Device::fromDevice(dev); delete device; return 0; } bool IsCuttlefish() { return android::base::GetProperty("ro.hardware.vulkan", "") == "pastel"; } } // namespace Device::Device() { DEBUG_LOG("%s", __FUNCTION__); common.tag = HARDWARE_DEVICE_TAG; common.version = HWC_DEVICE_API_VERSION_2_0; common.close = CloseHook; hwc2_device_t::getCapabilities = getCapabilitiesHook; hwc2_device_t::getFunction = getFunctionHook; } HWC2::Error Device::init() { DEBUG_LOG("%s", __FUNCTION__); if (IsCuttlefish()) { mComposer = std::make_unique(); } else { mComposer = std::make_unique(); } HWC2::Error error = mComposer->init( [this](bool connected, uint32_t id, uint32_t width, uint32_t height, uint32_t dpiX, uint32_t dpiY, uint32_t refreshRate) { handleHotplug(connected, id, width, height, dpiX, dpiY, refreshRate); }); if (error != HWC2::Error::None) { ALOGE("%s failed to initialize Composer", __FUNCTION__); return HWC2::Error::NoResources; } return HWC2::Error::None; } Device::~Device() { DEBUG_LOG("%s", __FUNCTION__); HWC2::Error error = HWC2::Error::None; error = destroyDisplays(); if (error != HWC2::Error::None) { ALOGE("%s failed to destroy displays", __FUNCTION__); } } HWC2::Error Device::createDisplays() { DEBUG_LOG("%s", __FUNCTION__); std::unique_lock lock(mStateMutex); if (!mComposer) { ALOGE("%s composer not initialized!", __FUNCTION__); return HWC2::Error::NoResources; } auto addDisplayLockedFn = [this](std::unique_ptr display) { auto displayId = display->getId(); DEBUG_LOG("%s: adding display:%" PRIu64, __FUNCTION__, displayId); mDisplays.emplace(displayId, std::move(display)); return HWC2::Error::None; }; HWC2::Error error = mComposer->createDisplays(this, addDisplayLockedFn); if (error != HWC2::Error::None) { ALOGE("%s composer failed to create displays", __FUNCTION__); return error; } return HWC2::Error::None; } HWC2::Error Device::createDisplay(uint32_t displayId, uint32_t width, uint32_t height, uint32_t dpiX, uint32_t dpiY, uint32_t refreshRate) { if (!mComposer) { ALOGE("%s composer not initialized!", __FUNCTION__); return HWC2::Error::NoResources; } auto addDisplayLockedFn = [this](std::unique_ptr display) { auto displayId = display->getId(); DEBUG_LOG("%s: adding display:%" PRIu64, __FUNCTION__, displayId); mDisplays.emplace(displayId, std::move(display)); return HWC2::Error::None; }; HWC2::Error error = mComposer->createDisplay(this, displayId, width, height, dpiX, dpiY, refreshRate, addDisplayLockedFn); if (error != HWC2::Error::None) { ALOGE("%s composer failed to create displays", __FUNCTION__); return error; } return HWC2::Error::None; } HWC2::Error Device::destroyDisplays() { DEBUG_LOG("%s", __FUNCTION__); std::unique_lock lock(mStateMutex); if (!mComposer) { ALOGE("%s composer not initialized!", __FUNCTION__); return HWC2::Error::NoResources; } for (auto& [displayId, displayPtr] : mDisplays) { HWC2::Error error = mComposer->onDisplayDestroy(displayPtr.get()); if (error != HWC2::Error::None) { ALOGE("%s composer failed to destroy displays", __FUNCTION__); return error; } displayPtr.reset(); } mDisplays.clear(); return HWC2::Error::None; } void Device::getCapabilities(uint32_t* outCount, int32_t* outCapabilities) { DEBUG_LOG("%s", __FUNCTION__); if (outCapabilities == nullptr) { *outCount = mCapabilities.size(); return; } auto capabilityIter = mCapabilities.cbegin(); for (size_t i = 0; i < *outCount; ++i) { if (capabilityIter == mCapabilities.cend()) { return; } outCapabilities[i] = static_cast(*capabilityIter); ++capabilityIter; } } /*static*/ void Device::getCapabilitiesHook(hwc2_device_t* dev, uint32_t* outCount, int32_t* outCapabilities) { DEBUG_LOG("%s", __FUNCTION__); Device* device = Device::fromDevice(dev); device->getCapabilities(outCount, outCapabilities); } hwc2_function_pointer_t Device::getFunction(int32_t desc) { const auto func = static_cast(desc); const auto funcString = to_string(func); DEBUG_LOG("%s(%s)", __FUNCTION__, funcString.c_str()); switch (func) { // Device functions. case HWC2::FunctionDescriptor::CreateVirtualDisplay: return asFP( DeviceHook); case HWC2::FunctionDescriptor::DestroyVirtualDisplay: return asFP( DeviceHook); case HWC2::FunctionDescriptor::Dump: return asFP(DeviceHook); case HWC2::FunctionDescriptor::GetMaxVirtualDisplayCount: return asFP( DeviceHook); case HWC2::FunctionDescriptor::RegisterCallback: return asFP( DeviceHook); // Display functions case HWC2::FunctionDescriptor::AcceptDisplayChanges: return asFP( displayHook); case HWC2::FunctionDescriptor::CreateLayer: return asFP( displayHook); case HWC2::FunctionDescriptor::DestroyLayer: return asFP( displayHook); case HWC2::FunctionDescriptor::GetActiveConfig: return asFP( displayHook); case HWC2::FunctionDescriptor::GetChangedCompositionTypes: return asFP( displayHook); case HWC2::FunctionDescriptor::GetColorModes: return asFP( displayHook); case HWC2::FunctionDescriptor::GetDisplayAttribute: return asFP( displayHook); case HWC2::FunctionDescriptor::GetDisplayConfigs: return asFP( displayHook); case HWC2::FunctionDescriptor::GetDisplayName: return asFP( displayHook); case HWC2::FunctionDescriptor::GetDisplayRequests: return asFP( displayHook); case HWC2::FunctionDescriptor::GetDisplayType: return asFP( displayHook); case HWC2::FunctionDescriptor::GetDozeSupport: return asFP( displayHook); case HWC2::FunctionDescriptor::GetHdrCapabilities: return asFP( displayHook); case HWC2::FunctionDescriptor::GetReleaseFences: return asFP( displayHook); case HWC2::FunctionDescriptor::PresentDisplay: return asFP( displayHook); case HWC2::FunctionDescriptor::SetActiveConfig: return asFP( displayHook); case HWC2::FunctionDescriptor::SetClientTarget: return asFP( displayHook); case HWC2::FunctionDescriptor::SetColorMode: return asFP( displayHook); case HWC2::FunctionDescriptor::SetColorTransform: return asFP( displayHook); case HWC2::FunctionDescriptor::SetOutputBuffer: return asFP( displayHook); case HWC2::FunctionDescriptor::SetPowerMode: return asFP( displayHook); case HWC2::FunctionDescriptor::SetVsyncEnabled: return asFP( displayHook); case HWC2::FunctionDescriptor::ValidateDisplay: return asFP( displayHook); case HWC2::FunctionDescriptor::GetClientTargetSupport: return asFP( displayHook); case HWC2::FunctionDescriptor::GetDisplayIdentificationData: return asFP( displayHook); case HWC2::FunctionDescriptor::GetDisplayCapabilities: return asFP( displayHook); case HWC2::FunctionDescriptor::GetDisplayBrightnessSupport: return asFP( displayHook); case HWC2::FunctionDescriptor::SetDisplayBrightness: return asFP( displayHook); // Layer functions case HWC2::FunctionDescriptor::SetCursorPosition: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerBuffer: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerSurfaceDamage: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerBlendMode: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerColor: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerCompositionType: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerDataspace: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerDisplayFrame: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerPlaneAlpha: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerSidebandStream: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerSourceCrop: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerTransform: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerVisibleRegion: return asFP( layerHook); case HWC2::FunctionDescriptor::SetLayerZOrder: return asFP( displayHook); default: ALOGE("GetFunction: Unknown function descriptor: %d", static_cast(desc)); return nullptr; } } /*static*/ hwc2_function_pointer_t Device::getFunctionHook(hwc2_device_t* dev, int32_t desc) { Device* device = Device::fromDevice(dev); return device->getFunction(desc); } // Device functions HWC2::Error Device::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/, int32_t* /*format*/, hwc2_display_t* /*outDisplay*/) { DEBUG_LOG("%s", __FUNCTION__); // TODO: VirtualDisplay support return HWC2::Error::None; } HWC2::Error Device::destroyVirtualDisplay(hwc2_display_t /*displayId*/) { DEBUG_LOG("%s", __FUNCTION__); // TODO: VirtualDisplay support return HWC2::Error::None; } void Device::dump(uint32_t* /*outSize*/, char* /*outBuffer*/) { DEBUG_LOG("%s", __FUNCTION__); // TODO: return; } uint32_t Device::getMaxVirtualDisplayCount() { DEBUG_LOG("%s", __FUNCTION__); // TODO: VirtualDisplay support return 0; } static bool IsHandledCallback(HWC2::Callback descriptor) { switch (descriptor) { case HWC2::Callback::Hotplug: { return true; } case HWC2::Callback::Refresh: { return true; } case HWC2::Callback::Vsync: { return true; } case HWC2::Callback::Vsync_2_4: { return false; } case HWC2::Callback::VsyncPeriodTimingChanged: { return false; } case HWC2::Callback::Invalid: { return false; } case HWC2::Callback::SeamlessPossible: { return false; } } return false; } HWC2::Error Device::registerCallback(int32_t desc, hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) { const auto callbackType = static_cast(desc); const auto callbackTypeString = to_string(callbackType); DEBUG_LOG("%s callback %s", __FUNCTION__, callbackTypeString.c_str()); if (!IsHandledCallback(callbackType)) { ALOGE("%s unhandled callback: %s", __FUNCTION__, callbackTypeString.c_str()); return HWC2::Error::BadParameter; } std::unique_lock lock(mStateMutex); if (pointer != nullptr) { mCallbacks[callbackType] = {callbackData, pointer}; } else { mCallbacks.erase(callbackType); return HWC2::Error::None; } if (callbackType == HWC2::Callback::Hotplug) { // Callback without the state lock held lock.unlock(); auto hotplug = reinterpret_cast(pointer); auto hotplugConnect = static_cast(HWC2::Connection::Connected); for (const auto& [displayId, display] : mDisplays) { ALOGI("%s hotplug connecting display:%" PRIu64, __FUNCTION__, displayId); hotplug(callbackData, displayId, hotplugConnect); } } return HWC2::Error::None; } bool Device::handleHotplug(bool connected, uint32_t id, uint32_t width, uint32_t height, uint32_t dpiX, uint32_t dpiY, uint32_t refreshRate) { std::unique_lock lock(mStateMutex); if (mCallbacks[HWC2::Callback::Hotplug].pointer == nullptr) { return false; } auto hotplug = reinterpret_cast( mCallbacks[HWC2::Callback::Hotplug].pointer); auto hotplugConnect = static_cast(HWC2::Connection::Connected); auto hotplugDisconnect = static_cast(HWC2::Connection::Disconnected); Display* display = getDisplay(id); if (display) { // if existed, disconnect first ALOGD("callback hotplugDisconnect display %" PRIu32, id); hotplug(mCallbacks[HWC2::Callback::Hotplug].data, id, hotplugDisconnect); display->lock(); mComposer->onDisplayDestroy(display); display->unlock(); } if (connected) { createDisplay(id, width, height, dpiX, dpiY, refreshRate); ALOGD("callback hotplugConnect display %" PRIu32 " width %" PRIu32 " height %" PRIu32 " dpiX %" PRIu32 " dpiY %" PRIu32 "fps %" PRIu32, id, width, height, dpiX, dpiY, refreshRate); hotplug(mCallbacks[HWC2::Callback::Hotplug].data, id, hotplugConnect); }; return true; } Display* Device::getDisplay(hwc2_display_t id) { auto display = mDisplays.find(id); if (display == mDisplays.end()) { ALOGW("Failed to get display for id=%d", (uint32_t)id); return nullptr; } return display->second.get(); } static int OpenDevice(const struct hw_module_t* module, const char* name, struct hw_device_t** dev) { DEBUG_LOG("%s ", __FUNCTION__); if (strcmp(name, HWC_HARDWARE_COMPOSER)) { ALOGE("Invalid module name- %s", name); return -EINVAL; } std::unique_ptr device = std::make_unique(); HWC2::Error error = device->init(); if (error != HWC2::Error::None) { ALOGE("%s: failed to initialize device", __FUNCTION__); return -EINVAL; } error = device->createDisplays(); if (error != HWC2::Error::None) { ALOGE("%s: failed to initialize device displays.", __FUNCTION__); return -EINVAL; } device->common.module = const_cast(module); *dev = &device.release()->common; return 0; } } // namespace android static struct hw_module_methods_t hwc2_module_methods = { .open = android::OpenDevice, }; hw_module_t HAL_MODULE_INFO_SYM = { .tag = HARDWARE_MODULE_TAG, .version_major = 2, .version_minor = 3, .id = HWC_HARDWARE_MODULE_ID, .name = "goldfish HWC2 module", .author = "The Android Open Source Project", .methods = &hwc2_module_methods, .dso = NULL, .reserved = {0}, };