| /* |
| * Copyright 2018 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 "EmuHWC2.h" |
| //#define LOG_NDEBUG 0 |
| //#define LOG_NNDEBUG 0 |
| #undef LOG_TAG |
| #define LOG_TAG "EmuHWC2" |
| |
| #include <errno.h> |
| #include <cutils/properties.h> |
| #include <log/log.h> |
| #include <sync/sync.h> |
| |
| #include <EGL/egl.h> |
| #include <EGL/eglext.h> |
| #include <ui/GraphicBuffer.h> |
| #include <ui/GraphicBufferAllocator.h> |
| |
| #include "../egl/goldfish_sync.h" |
| |
| #include "ThreadInfo.h" |
| |
| #if defined(LOG_NNDEBUG) && LOG_NNDEBUG == 0 |
| #define ALOGVV ALOGV |
| #else |
| #define ALOGVV(...) ((void)0) |
| #endif |
| |
| template <typename PFN, typename T> |
| static hwc2_function_pointer_t asFP(T function) |
| { |
| static_assert(std::is_same<PFN, T>::value, "Incompatible function pointer"); |
| return reinterpret_cast<hwc2_function_pointer_t>(function); |
| } |
| |
| static std::unique_ptr<HostConnection> sHostCon; |
| |
| static HostConnection* createOrGetHostConnection() { |
| if (!sHostCon) { |
| sHostCon = HostConnection::createUnique(); |
| } |
| return sHostCon.get(); |
| } |
| |
| #define DEFINE_AND_VALIDATE_HOST_CONNECTION \ |
| HostConnection *hostCon = createOrGetHostConnection(); \ |
| if (!hostCon) { \ |
| ALOGE("EmuHWC2: Failed to get host connection\n"); \ |
| return Error::NoResources; \ |
| } \ |
| ExtendedRCEncoderContext *rcEnc = hostCon->rcEncoder(); \ |
| if (!rcEnc) { \ |
| ALOGE("EmuHWC2: Failed to get renderControl encoder context\n"); \ |
| return Error::NoResources; \ |
| } |
| |
| using namespace HWC2; |
| |
| namespace android { |
| |
| EmuHWC2::EmuHWC2() |
| : mStateMutex() |
| { |
| common.tag = HARDWARE_DEVICE_TAG; |
| common.version = HWC_DEVICE_API_VERSION_2_0; |
| common.close = closeHook; |
| getCapabilities = getCapabilitiesHook; |
| getFunction = getFunctionHook; |
| populateCapabilities(); |
| initDisplayParameters(); |
| } |
| |
| Error EmuHWC2::initDisplayParameters() { |
| DEFINE_AND_VALIDATE_HOST_CONNECTION |
| hostCon->lock(); |
| |
| mDisplayWidth = rcEnc->rcGetFBParam(rcEnc, FB_WIDTH); |
| mDisplayHeight = rcEnc->rcGetFBParam(rcEnc, FB_HEIGHT); |
| mDisplayDpiX = rcEnc->rcGetFBParam(rcEnc, FB_XDPI); |
| mDisplayDpiY = rcEnc->rcGetFBParam(rcEnc, FB_YDPI); |
| |
| hostCon->unlock(); |
| |
| return HWC2::Error::None; |
| } |
| |
| void EmuHWC2::doGetCapabilities(uint32_t* outCount, int32_t* outCapabilities) { |
| 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<int32_t>(*capabilityIter); |
| ++capabilityIter; |
| } |
| } |
| |
| hwc2_function_pointer_t EmuHWC2::doGetFunction( |
| FunctionDescriptor descriptor) { |
| switch(descriptor) { |
| case FunctionDescriptor::CreateVirtualDisplay: |
| return asFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>( |
| createVirtualDisplayHook); |
| case FunctionDescriptor::DestroyVirtualDisplay: |
| return asFP<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>( |
| destroyVirtualDisplayHook); |
| case FunctionDescriptor::Dump: |
| return asFP<HWC2_PFN_DUMP>(dumpHook); |
| case FunctionDescriptor::GetMaxVirtualDisplayCount: |
| return asFP<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>( |
| getMaxVirtualDisplayCountHook); |
| case FunctionDescriptor::RegisterCallback: |
| return asFP<HWC2_PFN_REGISTER_CALLBACK>(registerCallbackHook); |
| |
| // Display functions |
| case FunctionDescriptor::AcceptDisplayChanges: |
| return asFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>( |
| displayHook<decltype(&Display::acceptChanges), |
| &Display::acceptChanges>); |
| case FunctionDescriptor::CreateLayer: |
| return asFP<HWC2_PFN_CREATE_LAYER>( |
| displayHook<decltype(&Display::createLayer), |
| &Display::createLayer, hwc2_layer_t*>); |
| case FunctionDescriptor::DestroyLayer: |
| return asFP<HWC2_PFN_DESTROY_LAYER>( |
| displayHook<decltype(&Display::destroyLayer), |
| &Display::destroyLayer, hwc2_layer_t>); |
| case FunctionDescriptor::GetActiveConfig: |
| return asFP<HWC2_PFN_GET_ACTIVE_CONFIG>( |
| displayHook<decltype(&Display::getActiveConfig), |
| &Display::getActiveConfig, hwc2_config_t*>); |
| case FunctionDescriptor::GetChangedCompositionTypes: |
| return asFP<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>( |
| displayHook<decltype(&Display::getChangedCompositionTypes), |
| &Display::getChangedCompositionTypes, uint32_t*, |
| hwc2_layer_t*, int32_t*>); |
| case FunctionDescriptor::GetColorModes: |
| return asFP<HWC2_PFN_GET_COLOR_MODES>( |
| displayHook<decltype(&Display::getColorModes), |
| &Display::getColorModes, uint32_t*, int32_t*>); |
| case FunctionDescriptor::GetDisplayAttribute: |
| return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>( |
| displayHook<decltype(&Display::getDisplayAttribute), |
| &Display::getDisplayAttribute, hwc2_config_t, |
| int32_t, int32_t*>); |
| case FunctionDescriptor::GetDisplayConfigs: |
| return asFP<HWC2_PFN_GET_DISPLAY_CONFIGS>( |
| displayHook<decltype(&Display::getConfigs), |
| &Display::getConfigs, uint32_t*, hwc2_config_t*>); |
| case FunctionDescriptor::GetDisplayName: |
| return asFP<HWC2_PFN_GET_DISPLAY_NAME>( |
| displayHook<decltype(&Display::getName), |
| &Display::getName, uint32_t*, char*>); |
| case FunctionDescriptor::GetDisplayRequests: |
| return asFP<HWC2_PFN_GET_DISPLAY_REQUESTS>( |
| displayHook<decltype(&Display::getRequests), |
| &Display::getRequests, int32_t*, uint32_t*, hwc2_layer_t*, |
| int32_t*>); |
| case FunctionDescriptor::GetDisplayType: |
| return asFP<HWC2_PFN_GET_DISPLAY_TYPE>( |
| displayHook<decltype(&Display::getType), |
| &Display::getType, int32_t*>); |
| case FunctionDescriptor::GetDozeSupport: |
| return asFP<HWC2_PFN_GET_DOZE_SUPPORT>( |
| displayHook<decltype(&Display::getDozeSupport), |
| &Display::getDozeSupport, int32_t*>); |
| case FunctionDescriptor::GetHdrCapabilities: |
| return asFP<HWC2_PFN_GET_HDR_CAPABILITIES>( |
| displayHook<decltype(&Display::getHdrCapabilities), |
| &Display::getHdrCapabilities, uint32_t*, int32_t*, float*, |
| float*, float*>); |
| case FunctionDescriptor::GetReleaseFences: |
| return asFP<HWC2_PFN_GET_RELEASE_FENCES>( |
| displayHook<decltype(&Display::getReleaseFences), |
| &Display::getReleaseFences, uint32_t*, hwc2_layer_t*, |
| int32_t*>); |
| case FunctionDescriptor::PresentDisplay: |
| return asFP<HWC2_PFN_PRESENT_DISPLAY>( |
| displayHook<decltype(&Display::present), |
| &Display::present, int32_t*>); |
| case FunctionDescriptor::SetActiveConfig: |
| return asFP<HWC2_PFN_SET_ACTIVE_CONFIG>( |
| displayHook<decltype(&Display::setActiveConfig), |
| &Display::setActiveConfig, hwc2_config_t>); |
| case FunctionDescriptor::SetClientTarget: |
| return asFP<HWC2_PFN_SET_CLIENT_TARGET>( |
| displayHook<decltype(&Display::setClientTarget), |
| &Display::setClientTarget, buffer_handle_t, int32_t, |
| int32_t, hwc_region_t>); |
| case FunctionDescriptor::SetColorMode: |
| return asFP<HWC2_PFN_SET_COLOR_MODE>( |
| displayHook<decltype(&Display::setColorMode), |
| &Display::setColorMode, int32_t>); |
| case FunctionDescriptor::SetColorTransform: |
| return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>( |
| displayHook<decltype(&Display::setColorTransform), |
| &Display::setColorTransform, const float*, int32_t>); |
| case FunctionDescriptor::SetOutputBuffer: |
| return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>( |
| displayHook<decltype(&Display::setOutputBuffer), |
| &Display::setOutputBuffer, buffer_handle_t, int32_t>); |
| case FunctionDescriptor::SetPowerMode: |
| return asFP<HWC2_PFN_SET_POWER_MODE>( |
| displayHook<decltype(&Display::setPowerMode), |
| &Display::setPowerMode, int32_t>); |
| case FunctionDescriptor::SetVsyncEnabled: |
| return asFP<HWC2_PFN_SET_VSYNC_ENABLED>( |
| displayHook<decltype(&Display::setVsyncEnabled), |
| &Display::setVsyncEnabled, int32_t>); |
| case FunctionDescriptor::ValidateDisplay: |
| return asFP<HWC2_PFN_VALIDATE_DISPLAY>( |
| displayHook<decltype(&Display::validate), |
| &Display::validate, uint32_t*, uint32_t*>); |
| case FunctionDescriptor::GetClientTargetSupport: |
| return asFP<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>( |
| displayHook<decltype(&Display::getClientTargetSupport), |
| &Display::getClientTargetSupport, uint32_t, uint32_t, |
| int32_t, int32_t>); |
| // 2.3 required functions |
| case FunctionDescriptor::GetDisplayIdentificationData: |
| return asFP<HWC2_PFN_GET_DISPLAY_IDENTIFICATION_DATA>( |
| displayHook<decltype(&Display::getDisplayIdentificationData), |
| &Display::getDisplayIdentificationData, uint8_t*, uint32_t*, uint8_t*>); |
| case FunctionDescriptor::GetDisplayCapabilities: |
| return asFP<HWC2_PFN_GET_DISPLAY_CAPABILITIES>( |
| displayHook<decltype(&Display::getDisplayCapabilities), |
| &Display::getDisplayCapabilities, uint32_t*, uint32_t*>); |
| case FunctionDescriptor::GetDisplayBrightnessSupport: |
| return asFP<HWC2_PFN_GET_DISPLAY_BRIGHTNESS_SUPPORT>( |
| displayHook<decltype(&Display::getDisplayBrightnessSupport), |
| &Display::getDisplayBrightnessSupport, bool*>); |
| case FunctionDescriptor::SetDisplayBrightness: |
| return asFP<HWC2_PFN_SET_DISPLAY_BRIGHTNESS>( |
| displayHook<decltype(&Display::setDisplayBrightness), |
| &Display::setDisplayBrightness, float>); |
| // Layer functions |
| case FunctionDescriptor::SetCursorPosition: |
| return asFP<HWC2_PFN_SET_CURSOR_POSITION>( |
| layerHook<decltype(&Layer::setCursorPosition), |
| &Layer::setCursorPosition, int32_t, int32_t>); |
| case FunctionDescriptor::SetLayerBuffer: |
| return asFP<HWC2_PFN_SET_LAYER_BUFFER>( |
| layerHook<decltype(&Layer::setBuffer), &Layer::setBuffer, |
| buffer_handle_t, int32_t>); |
| case FunctionDescriptor::SetLayerSurfaceDamage: |
| return asFP<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>( |
| layerHook<decltype(&Layer::setSurfaceDamage), |
| &Layer::setSurfaceDamage, hwc_region_t>); |
| |
| // Layer state functions |
| case FunctionDescriptor::SetLayerBlendMode: |
| return asFP<HWC2_PFN_SET_LAYER_BLEND_MODE>( |
| layerHook<decltype(&Layer::setBlendMode), |
| &Layer::setBlendMode, int32_t>); |
| case FunctionDescriptor::SetLayerColor: |
| return asFP<HWC2_PFN_SET_LAYER_COLOR>( |
| layerHook<decltype(&Layer::setColor), &Layer::setColor, |
| hwc_color_t>); |
| case FunctionDescriptor::SetLayerCompositionType: |
| return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>( |
| layerHook<decltype(&Layer::setCompositionType), |
| &Layer::setCompositionType, int32_t>); |
| case FunctionDescriptor::SetLayerDataspace: |
| return asFP<HWC2_PFN_SET_LAYER_DATASPACE>( |
| layerHook<decltype(&Layer::setDataspace), |
| &Layer::setDataspace, int32_t>); |
| case FunctionDescriptor::SetLayerDisplayFrame: |
| return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>( |
| layerHook<decltype(&Layer::setDisplayFrame), |
| &Layer::setDisplayFrame, hwc_rect_t>); |
| case FunctionDescriptor::SetLayerPlaneAlpha: |
| return asFP<HWC2_PFN_SET_LAYER_PLANE_ALPHA>( |
| layerHook<decltype(&Layer::setPlaneAlpha), |
| &Layer::setPlaneAlpha, float>); |
| case FunctionDescriptor::SetLayerSidebandStream: |
| return asFP<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>( |
| layerHook<decltype(&Layer::setSidebandStream), |
| &Layer::setSidebandStream, const native_handle_t*>); |
| case FunctionDescriptor::SetLayerSourceCrop: |
| return asFP<HWC2_PFN_SET_LAYER_SOURCE_CROP>( |
| layerHook<decltype(&Layer::setSourceCrop), |
| &Layer::setSourceCrop, hwc_frect_t>); |
| case FunctionDescriptor::SetLayerTransform: |
| return asFP<HWC2_PFN_SET_LAYER_TRANSFORM>( |
| layerHook<decltype(&Layer::setTransform), |
| &Layer::setTransform, int32_t>); |
| case FunctionDescriptor::SetLayerVisibleRegion: |
| return asFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>( |
| layerHook<decltype(&Layer::setVisibleRegion), |
| &Layer::setVisibleRegion, hwc_region_t>); |
| case FunctionDescriptor::SetLayerZOrder: |
| return asFP<HWC2_PFN_SET_LAYER_Z_ORDER>( |
| displayHook<decltype(&Display::updateLayerZ), |
| &Display::updateLayerZ, hwc2_layer_t, uint32_t>); |
| |
| default: |
| ALOGE("doGetFunction: Unknown function descriptor: %d (%s)", |
| static_cast<int32_t>(descriptor), |
| to_string(descriptor).c_str()); |
| return nullptr; |
| } |
| } |
| |
| |
| // Device functions |
| |
| Error EmuHWC2::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/, |
| int32_t* /*format*/, hwc2_display_t* /*outDisplay*/) { |
| ALOGVV("%s", __FUNCTION__); |
| //TODO: VirtualDisplay support |
| return Error::None; |
| } |
| |
| Error EmuHWC2::destroyVirtualDisplay(hwc2_display_t /*displayId*/) { |
| ALOGVV("%s", __FUNCTION__); |
| //TODO: VirtualDisplay support |
| return Error::None; |
| } |
| |
| void EmuHWC2::dump(uint32_t* /*outSize*/, char* /*outBuffer*/) { |
| ALOGVV("%s", __FUNCTION__); |
| //TODO: |
| return; |
| } |
| |
| uint32_t EmuHWC2::getMaxVirtualDisplayCount() { |
| ALOGVV("%s", __FUNCTION__); |
| //TODO: VirtualDisplay support |
| return 0; |
| } |
| |
| static bool isValid(Callback descriptor) { |
| switch (descriptor) { |
| case Callback::Hotplug: // Fall-through |
| case Callback::Refresh: // Fall-through |
| case Callback::Vsync: return true; |
| default: return false; |
| } |
| } |
| |
| Error EmuHWC2::registerCallback(Callback descriptor, |
| hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) { |
| ALOGVV("%s", __FUNCTION__); |
| if (!isValid(descriptor)) { |
| ALOGE("registerCallback: Unkown function descriptor: %d", |
| static_cast<int32_t>(descriptor)); |
| return Error::BadParameter; |
| } |
| ALOGV("registerCallback(%s, %p, %p)", to_string(descriptor).c_str(), |
| callbackData, pointer); |
| |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (pointer != nullptr) { |
| mCallbacks[descriptor] = {callbackData, pointer}; |
| } |
| else { |
| ALOGV("unregisterCallback(%s)", to_string(descriptor).c_str()); |
| mCallbacks.erase(descriptor); |
| return Error::None; |
| } |
| |
| // Callback without the state lock held |
| if (descriptor == Callback::Hotplug) { |
| lock.unlock(); |
| auto hotplug = reinterpret_cast<HWC2_PFN_VSYNC>(pointer); |
| for (const auto& iter : mDisplays) { |
| hotplug(callbackData, iter.first, static_cast<int32_t>(Connection::Connected)); |
| } |
| } |
| |
| return Error::None; |
| } |
| |
| const native_handle_t* EmuHWC2::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 EmuHWC2::freeDisplayColorBuffer(const native_handle_t* h) { |
| GraphicBufferAllocator::get().free(h); |
| } |
| |
| // Display functions |
| |
| #define VSYNC_PERIOD_PROP "ro.kernel.qemu.vsync" |
| |
| static int getVsyncPeriodFromProperty() { |
| char displaysValue[PROPERTY_VALUE_MAX] = ""; |
| property_get(VSYNC_PERIOD_PROP, displaysValue, ""); |
| bool isValid = displaysValue[0] != '\0'; |
| |
| if (!isValid) return 60; |
| |
| long vsyncPeriodParsed = strtol(displaysValue, 0, 10); |
| |
| // On failure, strtol returns 0. Also, there's no reason to have 0 |
| // as the vsync period. |
| if (!vsyncPeriodParsed) return 60; |
| |
| return static_cast<int>(vsyncPeriodParsed); |
| } |
| |
| std::atomic<hwc2_display_t> EmuHWC2::Display::sNextId(0); |
| |
| EmuHWC2::Display::Display(EmuHWC2& device, DisplayType type, int width, int height) |
| : mDevice(device), |
| mId(sNextId++), |
| mHostDisplayId(0), |
| mName(), |
| mType(type), |
| mPowerMode(PowerMode::Off), |
| mVsyncEnabled(Vsync::Invalid), |
| mVsyncPeriod(1000*1000*1000/getVsyncPeriodFromProperty()), // vsync is 60 hz |
| mVsyncThread(*this), |
| mClientTarget(), |
| mChanges(), |
| mLayers(), |
| mReleaseLayerIds(), |
| mReleaseFences(), |
| mConfigs(), |
| mActiveConfig(nullptr), |
| mColorModes(), |
| mSetColorTransform(false), |
| mStateMutex() { |
| mVsyncThread.run("", ANDROID_PRIORITY_URGENT_DISPLAY); |
| mTargetCb = device.allocateDisplayColorBuffer(width, height); |
| } |
| |
| EmuHWC2::Display::~Display() { |
| mDevice.freeDisplayColorBuffer(mTargetCb); |
| } |
| |
| Error EmuHWC2::Display::acceptChanges() { |
| ALOGVV("%s: displayId %u", __FUNCTION__, (uint32_t)mId); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (!mChanges) { |
| ALOGW("%s: displayId %u acceptChanges failed, not validated", |
| __FUNCTION__, (uint32_t)mId); |
| return Error::NotValidated; |
| } |
| |
| |
| for (auto& change : mChanges->getTypeChanges()) { |
| auto layerId = change.first; |
| auto type = change.second; |
| if (mDevice.mLayers.count(layerId) == 0) { |
| // This should never happen but somehow does. |
| ALOGW("Cannot accept change for unknown layer %u", |
| (uint32_t)layerId); |
| continue; |
| } |
| auto layer = mDevice.mLayers[layerId]; |
| layer->setCompositionType((int32_t)type); |
| } |
| |
| mChanges->clearTypeChanges(); |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::createLayer(hwc2_layer_t* outLayerId) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| auto layer = *mLayers.emplace(std::make_shared<Layer>(*this)); |
| mDevice.mLayers.emplace(std::make_pair(layer->getId(), layer)); |
| *outLayerId = layer->getId(); |
| ALOGV("%s: Display %u created layer %u", __FUNCTION__, (uint32_t)mId, |
| (uint32_t)(*outLayerId)); |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::destroyLayer(hwc2_layer_t layerId) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| const auto mapLayer = mDevice.mLayers.find(layerId); |
| if (mapLayer == mDevice.mLayers.end()) { |
| ALOGW("%s failed: no such layer, displayId %u layerId %u", |
| __FUNCTION__, (uint32_t)mId, (uint32_t)layerId); |
| return Error::BadLayer; |
| } |
| const auto layer = mapLayer->second; |
| mDevice.mLayers.erase(mapLayer); |
| const auto zRange = mLayers.equal_range(layer); |
| for (auto current = zRange.first; current != zRange.second; ++current) { |
| if (**current == *layer) { |
| current = mLayers.erase(current); |
| break; |
| } |
| } |
| ALOGV("%s: displayId %d layerId %d", __FUNCTION__, (uint32_t)mId, |
| (uint32_t)layerId); |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getActiveConfig(hwc2_config_t* outConfig) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (!mActiveConfig) { |
| ALOGW("%s: displayId %d %s", __FUNCTION__, (uint32_t)mId, |
| to_string(Error::BadConfig).c_str()); |
| return Error::BadConfig; |
| } |
| auto configId = mActiveConfig->getId(); |
| ALOGV("%s: displayId %d configId %d", __FUNCTION__, |
| (uint32_t)mId, (uint32_t)configId); |
| *outConfig = configId; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getDisplayAttribute(hwc2_config_t configId, |
| int32_t attribute, int32_t* outValue) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) { |
| ALOGW("%s: bad config (%u %u)", __FUNCTION__, (uint32_t)mId, configId); |
| return Error::BadConfig; |
| } |
| *outValue = mConfigs[configId]->getAttribute((Attribute)attribute); |
| ALOGV("%s: (%d %d) %s --> %d", __FUNCTION__, |
| (uint32_t)mId, (uint32_t)configId, |
| to_string((Attribute)attribute).c_str(), *outValue); |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getChangedCompositionTypes( |
| uint32_t* outNumElements, hwc2_layer_t* outLayers, int32_t* outTypes) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (!mChanges) { |
| ALOGW("display %u getChangedCompositionTypes failed: not validated", |
| (uint32_t)mId); |
| return Error::NotValidated; |
| } |
| |
| if ((outLayers == nullptr) || (outTypes == nullptr)) { |
| *outNumElements = mChanges->getTypeChanges().size(); |
| return Error::None; |
| } |
| |
| uint32_t numWritten = 0; |
| for (const auto& element : mChanges->getTypeChanges()) { |
| if (numWritten == *outNumElements) { |
| break; |
| } |
| auto layerId = element.first; |
| auto intType = static_cast<int32_t>(element.second); |
| ALOGV("%s: Adding layer %u %s", __FUNCTION__, (uint32_t)layerId, |
| to_string(element.second).c_str()); |
| outLayers[numWritten] = layerId; |
| outTypes[numWritten] = intType; |
| ++numWritten; |
| } |
| *outNumElements = numWritten; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getColorModes(uint32_t* outNumModes, |
| int32_t* outModes) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (!outModes) { |
| *outNumModes = mColorModes.size(); |
| return Error::None; |
| } |
| |
| // we only support HAL_COLOR_MODE_NATIVE so far |
| uint32_t numModes = std::min(*outNumModes, |
| static_cast<uint32_t>(mColorModes.size())); |
| std::copy_n(mColorModes.cbegin(), numModes, outModes); |
| *outNumModes = numModes; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getConfigs(uint32_t* outNumConfigs, |
| hwc2_config_t* outConfigs) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (!outConfigs) { |
| *outNumConfigs = mConfigs.size(); |
| return Error::None; |
| } |
| uint32_t numWritten = 0; |
| for (const auto config : mConfigs) { |
| if (numWritten == *outNumConfigs) { |
| break; |
| } |
| outConfigs[numWritten] = config->getId(); |
| ++numWritten; |
| } |
| *outNumConfigs = numWritten; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getDozeSupport(int32_t* outSupport) { |
| ALOGVV("%s", __FUNCTION__); |
| // We don't support so far |
| *outSupport = 0; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getHdrCapabilities(uint32_t* outNumTypes, |
| int32_t* /*outTypes*/, float* /*outMaxLuminance*/, |
| float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) { |
| ALOGVV("%s", __FUNCTION__); |
| // We don't support so far |
| *outNumTypes = 0; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getName(uint32_t* outSize, char* outName) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (!outName) { |
| *outSize = mName.size(); |
| return Error::None; |
| } |
| auto numCopied = mName.copy(outName, *outSize); |
| *outSize = numCopied; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getReleaseFences(uint32_t* outNumElements, |
| hwc2_layer_t* outLayers, int32_t* outFences) { |
| ALOGVV("%s", __FUNCTION__); |
| |
| *outNumElements = mReleaseLayerIds.size(); |
| |
| ALOGVV("%s. Got %u elements", __FUNCTION__, *outNumElements); |
| |
| if (*outNumElements && outLayers) { |
| ALOGVV("%s. export release layers", __FUNCTION__); |
| memcpy(outLayers, mReleaseLayerIds.data(), |
| sizeof(hwc2_layer_t) * (*outNumElements)); |
| } |
| |
| if (*outNumElements && outFences) { |
| ALOGVV("%s. export release fences", __FUNCTION__); |
| memcpy(outFences, mReleaseFences.data(), |
| sizeof(int32_t) * (*outNumElements)); |
| } |
| |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getRequests(int32_t* outDisplayRequests, |
| uint32_t* outNumElements, hwc2_layer_t* outLayers, |
| int32_t* outLayerRequests) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (!mChanges) { |
| return Error::NotValidated; |
| } |
| |
| if (outLayers == nullptr || outLayerRequests == nullptr) { |
| *outNumElements = mChanges->getNumLayerRequests(); |
| return Error::None; |
| } |
| |
| //TODO |
| // Display requests (HWC2::DisplayRequest) are not supported so far: |
| *outDisplayRequests = 0; |
| |
| uint32_t numWritten = 0; |
| for (const auto& request : mChanges->getLayerRequests()) { |
| if (numWritten == *outNumElements) { |
| break; |
| } |
| outLayers[numWritten] = request.first; |
| outLayerRequests[numWritten] = static_cast<int32_t>(request.second); |
| ++numWritten; |
| } |
| |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getType(int32_t* outType) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| *outType = (int32_t)mType; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::present(int32_t* outRetireFence) { |
| ALOGVV("%s", __FUNCTION__); |
| |
| *outRetireFence = -1; |
| |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (!mChanges || (mChanges->getNumTypes() > 0)) { |
| ALOGE("%s display(%u) set failed: not validated", __FUNCTION__, |
| (uint32_t)mId); |
| return Error::NotValidated; |
| } |
| mChanges.reset(); |
| |
| DEFINE_AND_VALIDATE_HOST_CONNECTION |
| hostCon->lock(); |
| bool hostCompositionV1 = rcEnc->hasHostCompositionV1(); |
| bool hostCompositionV2 = rcEnc->hasHostCompositionV2(); |
| hostCon->unlock(); |
| |
| // if we supports v2, then discard v1 |
| if (hostCompositionV2) { |
| hostCompositionV1 = false; |
| } |
| |
| if (hostCompositionV2 || hostCompositionV1) { |
| uint32_t numLayer = 0; |
| for (auto layer: mLayers) { |
| if (layer->getCompositionType() == Composition::Device || |
| layer->getCompositionType() == Composition::SolidColor) { |
| numLayer++; |
| } |
| } |
| |
| ALOGVV("present %d layers total %u layers", |
| numLayer, (uint32_t)mLayers.size()); |
| |
| mReleaseLayerIds.clear(); |
| mReleaseFences.clear(); |
| |
| if (numLayer == 0) { |
| ALOGW("No layers, exit, buffer %p", mClientTarget.getBuffer()); |
| if (mClientTarget.getBuffer()) { |
| post(hostCon, rcEnc, mClientTarget.getBuffer()); |
| *outRetireFence = mClientTarget.getFence(); |
| } |
| return Error::None; |
| } |
| |
| if (hostCompositionV1) { |
| if (mComposeMsg == nullptr || mComposeMsg->getLayerCnt() < numLayer) { |
| mComposeMsg.reset(new ComposeMsg(numLayer)); |
| } |
| } else { |
| if (mComposeMsg_v2 == nullptr || mComposeMsg_v2->getLayerCnt() < numLayer) { |
| mComposeMsg_v2.reset(new ComposeMsg_v2(numLayer)); |
| } |
| } |
| |
| // Handle the composition |
| ComposeDevice* p; |
| ComposeDevice_v2* p2; |
| ComposeLayer* l; |
| |
| if (hostCompositionV1) { |
| p = mComposeMsg->get(); |
| l = p->layer; |
| } else { |
| p2 = mComposeMsg_v2->get(); |
| l = p2->layer; |
| } |
| |
| for (auto layer: mLayers) { |
| if (layer->getCompositionType() != Composition::Device && |
| layer->getCompositionType() != 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() == Composition::Device) { |
| int fence = layer->getLayerBuffer().getFence(); |
| mReleaseLayerIds.push_back(layer->getId()); |
| 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->getLayerBuffer().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 = 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(mTargetCb); |
| p->numLayers = numLayer; |
| } else { |
| p2->version = 2; |
| p2->displayId = mHostDisplayId; |
| p2->targetHandle = hostCon->grallocHelper()->getHostHandle(mTargetCb); |
| p2->numLayers = numLayer; |
| } |
| |
| hostCon->lock(); |
| 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; |
| |
| hostCon->lock(); |
| rcEnc->rcCreateSyncKHR(rcEnc, EGL_SYNC_NATIVE_FENCE_ANDROID, |
| attribs, 2 * sizeof(EGLint), true /* destroy when signaled */, |
| &sync_handle, &thread_handle); |
| hostCon->unlock(); |
| |
| goldfish_sync_queue_work(mSyncDeviceFd, |
| sync_handle, thread_handle, &retire_fd); |
| |
| for (size_t i = 0; i < mReleaseLayerIds.size(); ++i) { |
| mReleaseFences.push_back(dup(retire_fd)); |
| } |
| |
| *outRetireFence = dup(retire_fd); |
| close(retire_fd); |
| hostCon->lock(); |
| rcEnc->rcDestroySyncKHR(rcEnc, sync_handle); |
| hostCon->unlock(); |
| } else { |
| // we set all layers Composition::Client, so do nothing. |
| post(hostCon, rcEnc, mClientTarget.getBuffer()); |
| *outRetireFence = mClientTarget.getFence(); |
| ALOGV("%s fallback to post, returns outRetireFence %d", |
| __FUNCTION__, *outRetireFence); |
| } |
| |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::setActiveConfig(hwc2_config_t configId) { |
| ALOGVV("%s %u", __FUNCTION__, (uint32_t)configId); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (configId > mConfigs.size() || !mConfigs[configId]->isOnDisplay(*this)) { |
| ALOGW("%s: bad config (%u %u)", __FUNCTION__, (uint32_t)mId, |
| (uint32_t)configId); |
| return Error::BadConfig; |
| } |
| auto config = mConfigs[configId]; |
| if (config == mActiveConfig) { |
| return Error::None; |
| } |
| |
| mActiveConfig = config; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::setClientTarget(buffer_handle_t target, |
| int32_t acquireFence, int32_t /*dataspace*/, hwc_region_t /*damage*/) { |
| ALOGVV("%s", __FUNCTION__); |
| |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| mClientTarget.setBuffer(target); |
| mClientTarget.setFence(acquireFence); |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::setColorMode(int32_t intMode) { |
| ALOGVV("%s %d", __FUNCTION__, intMode); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| auto mode = static_cast<android_color_mode_t>(intMode); |
| ALOGV("%s: (display %u mode %d)", __FUNCTION__, (uint32_t)mId, intMode); |
| if (mode == mActiveColorMode) { |
| return Error::None; |
| } |
| if (mColorModes.count(mode) == 0) { |
| ALOGE("%s: display %d Mode %d not found in mColorModes", |
| __FUNCTION__, (uint32_t)mId, intMode); |
| return Error::Unsupported; |
| } |
| mActiveColorMode = mode; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::setColorTransform(const float* /*matrix*/, |
| int32_t hint) { |
| ALOGVV("%s hint %d", __FUNCTION__, hint); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| //we force client composition if this is set |
| if (hint == 0 ) { |
| mSetColorTransform = false; |
| } |
| else { |
| mSetColorTransform = true; |
| } |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::setOutputBuffer(buffer_handle_t /*buffer*/, |
| int32_t /*releaseFence*/) { |
| ALOGVV("%s", __FUNCTION__); |
| //TODO: for virtual display |
| return Error::None; |
| } |
| |
| static bool isValid(PowerMode mode) { |
| switch (mode) { |
| case PowerMode::Off: // Fall-through |
| case PowerMode::DozeSuspend: // Fall-through |
| case PowerMode::Doze: // Fall-through |
| case PowerMode::On: return true; |
| default: return false; |
| } |
| } |
| |
| Error EmuHWC2::Display::setPowerMode(int32_t intMode) { |
| ALOGVV("%s", __FUNCTION__); |
| // Emulator always set screen ON |
| PowerMode mode = static_cast<PowerMode>(intMode); |
| if (!isValid(mode)) { |
| return Error::BadParameter; |
| } |
| if (mode == mPowerMode) { |
| return Error::None; |
| } |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| ALOGV("%s: (display %u mode %s)", __FUNCTION__, |
| (uint32_t)mId, to_string(mode).c_str()); |
| mPowerMode = mode; |
| return Error::None; |
| } |
| |
| static bool isValid(Vsync enable) { |
| switch (enable) { |
| case Vsync::Enable: // Fall-through |
| case Vsync::Disable: return true; |
| case Vsync::Invalid: return false; |
| } |
| } |
| |
| Error EmuHWC2::Display::setVsyncEnabled(int32_t intEnable) { |
| ALOGVV("%s %d", __FUNCTION__, intEnable); |
| Vsync enable = static_cast<Vsync>(intEnable); |
| if (!isValid(enable)) { |
| return Error::BadParameter; |
| } |
| if (enable == mVsyncEnabled) { |
| return Error::None; |
| } |
| |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| mVsyncEnabled = enable; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::validate(uint32_t* outNumTypes, |
| uint32_t* outNumRequests) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (!mChanges) { |
| mChanges.reset(new Changes); |
| DEFINE_AND_VALIDATE_HOST_CONNECTION |
| hostCon->lock(); |
| bool hostCompositionV1 = rcEnc->hasHostCompositionV1(); |
| bool hostCompositionV2 = rcEnc->hasHostCompositionV2(); |
| hostCon->unlock(); |
| |
| if (hostCompositionV1 || hostCompositionV2) { |
| // Support Device and SolidColor, otherwise, fallback all layers |
| // to Client |
| bool fallBack = false; |
| for (auto& layer : mLayers) { |
| if (layer->getCompositionType() == 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() == Composition::Client || |
| layer->getCompositionType() == Composition::Cursor || |
| layer->getCompositionType() == Composition::Sideband) { |
| ALOGW("%s: layer %u CompositionType %d, fallback", __FUNCTION__, |
| (uint32_t)layer->getId(), layer->getCompositionType()); |
| fallBack = true; |
| break; |
| } |
| } |
| if (mSetColorTransform) { |
| fallBack = true; |
| } |
| if (fallBack) { |
| for (auto& layer : mLayers) { |
| if (layer->getCompositionType() == Composition::Invalid) { |
| continue; |
| } |
| if (layer->getCompositionType() != Composition::Client) { |
| mChanges->addTypeChange(layer->getId(), |
| Composition::Client); |
| } |
| } |
| } |
| } |
| else { |
| for (auto& layer : mLayers) { |
| if (layer->getCompositionType() != Composition::Client) { |
| mChanges->addTypeChange(layer->getId(), |
| Composition::Client); |
| } |
| } |
| } |
| } |
| else { |
| ALOGE("Validate was called more than once!"); |
| } |
| |
| *outNumTypes = mChanges->getNumTypes(); |
| *outNumRequests = mChanges->getNumLayerRequests(); |
| ALOGV("%s: displayId %u types %u, requests %u", __FUNCTION__, |
| (uint32_t)mId, *outNumTypes, *outNumRequests); |
| return *outNumTypes > 0 ? Error::HasChanges : Error::None; |
| } |
| |
| Error EmuHWC2::Display::updateLayerZ(hwc2_layer_t layerId, uint32_t z) { |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| const auto mapLayer = mDevice.mLayers.find(layerId); |
| if (mapLayer == mDevice.mLayers.end()) { |
| ALOGE("%s failed to find layer %u", __FUNCTION__, (uint32_t)mId); |
| return Error::BadLayer; |
| } |
| |
| const auto layer = mapLayer->second; |
| const auto zRange = mLayers.equal_range(layer); |
| bool layerOnDisplay = false; |
| for (auto current = zRange.first; current != zRange.second; ++current) { |
| if (**current == *layer) { |
| if ((*current)->getZ() == z) { |
| // Don't change anything if the Z hasn't changed |
| return Error::None; |
| } |
| current = mLayers.erase(current); |
| layerOnDisplay = true; |
| break; |
| } |
| } |
| |
| if (!layerOnDisplay) { |
| ALOGE("%s failed to find layer %u on display", __FUNCTION__, |
| (uint32_t)mId); |
| return Error::BadLayer; |
| } |
| |
| layer->setZ(z); |
| mLayers.emplace(std::move(layer)); |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getClientTargetSupport(uint32_t width, uint32_t height, |
| int32_t format, int32_t dataspace){ |
| ALOGVV("%s", __FUNCTION__); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| if (mActiveConfig == nullptr) { |
| return Error::Unsupported; |
| } |
| |
| if (width == (uint32_t)mActiveConfig->getAttribute(Attribute::Width) && |
| height == (uint32_t)mActiveConfig->getAttribute(Attribute::Height) && |
| format == HAL_PIXEL_FORMAT_RGBA_8888 && |
| dataspace == HAL_DATASPACE_UNKNOWN) { |
| return Error::None; |
| } |
| |
| return Error::None; |
| } |
| |
| // thess EDIDs are carefully generated according to the EDID spec version 1.3, more info |
| // can be found from the following file: |
| // frameworks/native/services/surfaceflinger/DisplayHardware/DisplayIdentification.cpp |
| // approved pnp ids can be found here: https://uefi.org/pnp_id_list |
| // pnp id: GGL, name: EMU_display_0, last byte is checksum |
| // display id is local:8141603649153536 |
| static const uint8_t sEDID0[] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, |
| 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78, 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, |
| 0x12, 0x48, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
| 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, |
| 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, |
| 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x30, 0x00, 0x4b |
| }; |
| |
| // pnp id: GGL, name: EMU_display_1 |
| // display id is local:8140900251843329 |
| static const uint8_t sEDID1[] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, |
| 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78, 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, |
| 0x12, 0x48, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
| 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, |
| 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, |
| 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x31, 0x00, 0x3b |
| }; |
| |
| // pnp id: GGL, name: EMU_display_2 |
| // display id is local:8140940453066754 |
| static const uint8_t sEDID2[] = { |
| 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x1c, 0xec, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, |
| 0x1b, 0x10, 0x01, 0x03, 0x80, 0x50, 0x2d, 0x78, 0x0a, 0x0d, 0xc9, 0xa0, 0x57, 0x47, 0x98, 0x27, |
| 0x12, 0x48, 0x4c, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
| 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, |
| 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, |
| 0x00, 0x45, 0x4d, 0x55, 0x5f, 0x64, 0x69, 0x73, 0x70, 0x6c, 0x61, 0x79, 0x5f, 0x32, 0x00, 0x49 |
| }; |
| |
| #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
| |
| Error EmuHWC2::Display::getDisplayIdentificationData(uint8_t* outPort, |
| uint32_t* outDataSize, uint8_t* outData) { |
| ALOGVV("%s DisplayId %u", __FUNCTION__, (uint32_t)mId); |
| if (outPort == nullptr || outDataSize == nullptr) |
| return Error::BadParameter; |
| |
| uint32_t len = std::min(*outDataSize, (uint32_t)ARRAY_SIZE(sEDID0)); |
| if (outData != nullptr && len < (uint32_t)ARRAY_SIZE(sEDID0)) { |
| ALOGW("%s DisplayId %u, small buffer size: %u is specified", |
| __FUNCTION__, (uint32_t)mId, len); |
| } |
| *outDataSize = ARRAY_SIZE(sEDID0); |
| switch (mId) { |
| case 0: |
| *outPort = 0; |
| if (outData) |
| memcpy(outData, sEDID0, len); |
| break; |
| |
| case 1: |
| *outPort = 1; |
| if (outData) |
| memcpy(outData, sEDID1, len); |
| break; |
| |
| case 2: |
| *outPort = 2; |
| if (outData) |
| memcpy(outData, sEDID2, len); |
| break; |
| |
| default: |
| *outPort = (uint8_t)mId; |
| if (outData) { |
| memcpy(outData, sEDID2, len); |
| uint32_t size = ARRAY_SIZE(sEDID0); |
| // change the name to EMU_display_<mID> |
| // note the 3rd char from back is the number, _0, _1, _2, etc. |
| if (len >= size - 2) |
| outData[size-3] = '0' + (uint8_t)mId; |
| if (len >= size) { |
| // update the last byte, which is checksum byte |
| uint8_t checksum = -(uint8_t)std::accumulate( |
| outData, outData + size - 1, static_cast<uint8_t>(0)); |
| outData[size - 1] = checksum; |
| } |
| } |
| break; |
| } |
| |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getDisplayCapabilities(uint32_t* outNumCapabilities, |
| uint32_t* outCapabilities) { |
| if (outNumCapabilities == nullptr) { |
| return Error::None; |
| } |
| |
| bool brightness_support = true; |
| bool doze_support = true; |
| |
| uint32_t count = 1 + static_cast<uint32_t>(doze_support) + (brightness_support ? 1 : 0); |
| int index = 0; |
| if (outCapabilities != nullptr && (*outNumCapabilities >= count)) { |
| outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_SKIP_CLIENT_COLOR_TRANSFORM; |
| if (doze_support) { |
| outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_DOZE; |
| } |
| if (brightness_support) { |
| outCapabilities[index++] = HWC2_DISPLAY_CAPABILITY_BRIGHTNESS; |
| } |
| } |
| |
| *outNumCapabilities = count; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::getDisplayBrightnessSupport(bool *out_support) { |
| *out_support = false; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Display::setDisplayBrightness(float brightness) { |
| ALOGW("TODO: setDisplayBrightness() is not implemented yet: brightness=%f", brightness); |
| return Error::None; |
| } |
| |
| int EmuHWC2::Display::populatePrimaryConfigs(int width, int height, int dpiX, int dpiY) { |
| ALOGVV("%s DisplayId %u", __FUNCTION__, (uint32_t)mId); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| auto newConfig = std::make_shared<Config>(*this); |
| // vsync is 60 hz; |
| newConfig->setAttribute(Attribute::VsyncPeriod, mVsyncPeriod); |
| newConfig->setAttribute(Attribute::Width, width); |
| newConfig->setAttribute(Attribute::Height, height); |
| newConfig->setAttribute(Attribute::DpiX, dpiX * 1000); |
| newConfig->setAttribute(Attribute::DpiY, dpiY * 1000); |
| |
| newConfig->setId(static_cast<hwc2_config_t>(mConfigs.size())); |
| ALOGV("Found new config %d: %s", (uint32_t)newConfig->getId(), |
| newConfig->toString().c_str()); |
| mConfigs.emplace_back(std::move(newConfig)); |
| |
| // Only have single config so far, it is activeConfig |
| mActiveConfig = mConfigs[0]; |
| mActiveColorMode = HAL_COLOR_MODE_NATIVE; |
| mColorModes.emplace((android_color_mode_t)HAL_COLOR_MODE_NATIVE); |
| |
| mSyncDeviceFd = goldfish_sync_open(); |
| |
| return 0; |
| } |
| |
| void EmuHWC2::Display::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(); |
| } |
| |
| HWC2::Error EmuHWC2::Display::populateSecondaryConfigs(uint32_t width, uint32_t height, |
| uint32_t dpi, uint32_t idx) { |
| ALOGVV("%s DisplayId %u, width %u, height %u, dpi %u", |
| __FUNCTION__, (uint32_t)mId, width, height, dpi); |
| std::unique_lock<std::mutex> lock(mStateMutex); |
| |
| auto newConfig = std::make_shared<Config>(*this); |
| // vsync is 60 hz; |
| newConfig->setAttribute(Attribute::VsyncPeriod, mVsyncPeriod); |
| newConfig->setAttribute(Attribute::Width, width); |
| newConfig->setAttribute(Attribute::Height, height); |
| newConfig->setAttribute(Attribute::DpiX, dpi*1000); |
| newConfig->setAttribute(Attribute::DpiY, dpi*1000); |
| |
| int configId = mConfigs.size(); |
| newConfig->setId(static_cast<hwc2_config_t>(configId)); |
| ALOGV("Found new secondary config %d: %s", (uint32_t)newConfig->getId(), |
| newConfig->toString().c_str()); |
| mConfigs.emplace_back(std::move(newConfig)); |
| |
| mActiveConfig = mConfigs[configId]; |
| mActiveColorMode = HAL_COLOR_MODE_NATIVE; |
| mColorModes.emplace((android_color_mode_t)HAL_COLOR_MODE_NATIVE); |
| |
| uint32_t displayId = hostDisplayIdStart + idx; |
| DEFINE_AND_VALIDATE_HOST_CONNECTION |
| |
| hostCon->lock(); |
| rcEnc->rcDestroyDisplay(rcEnc, displayId); |
| rcEnc->rcCreateDisplay(rcEnc, &displayId); |
| rcEnc->rcSetDisplayPose(rcEnc, displayId, -1, -1, width, height); |
| hostCon->unlock(); |
| |
| if (displayId != hostDisplayIdStart + idx) { |
| ALOGE("Something wrong with host displayId allocation, want %d " |
| "allocated %d", hostDisplayIdStart + idx, displayId); |
| } |
| mHostDisplayId = displayId; |
| ALOGVV("%s: mHostDisplayId=%d", __FUNCTION__, mHostDisplayId); |
| |
| return HWC2::Error::None; |
| } |
| |
| |
| // Config functions |
| |
| void EmuHWC2::Display::Config::setAttribute(Attribute attribute, |
| int32_t value) { |
| mAttributes[attribute] = value; |
| } |
| |
| int32_t EmuHWC2::Display::Config::getAttribute(Attribute attribute) const { |
| if (mAttributes.count(attribute) == 0) { |
| return -1; |
| } |
| return mAttributes.at(attribute); |
| } |
| |
| std::string EmuHWC2::Display::Config::toString() const { |
| std::string output; |
| |
| const size_t BUFFER_SIZE = 100; |
| char buffer[BUFFER_SIZE] = {}; |
| auto writtenBytes = snprintf(buffer, BUFFER_SIZE, |
| "%u x %u", mAttributes.at(HWC2::Attribute::Width), |
| mAttributes.at(HWC2::Attribute::Height)); |
| output.append(buffer, writtenBytes); |
| |
| if (mAttributes.count(HWC2::Attribute::VsyncPeriod) != 0) { |
| std::memset(buffer, 0, BUFFER_SIZE); |
| writtenBytes = snprintf(buffer, BUFFER_SIZE, " @ %.1f Hz", |
| 1e9 / mAttributes.at(HWC2::Attribute::VsyncPeriod)); |
| output.append(buffer, writtenBytes); |
| } |
| |
| if (mAttributes.count(HWC2::Attribute::DpiX) != 0 && |
| mAttributes.at(HWC2::Attribute::DpiX) != -1) { |
| std::memset(buffer, 0, BUFFER_SIZE); |
| writtenBytes = snprintf(buffer, BUFFER_SIZE, |
| ", DPI: %.1f x %.1f", |
| mAttributes.at(HWC2::Attribute::DpiX) / 1000.0f, |
| mAttributes.at(HWC2::Attribute::DpiY) / 1000.0f); |
| output.append(buffer, writtenBytes); |
| } |
| |
| return output; |
| } |
| |
| // VsyncThread function |
| bool EmuHWC2::Display::VsyncThread::threadLoop() { |
| struct timespec rt; |
| if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) { |
| ALOGE("%s: error in vsync thread clock_gettime: %s", |
| __FUNCTION__, strerror(errno)); |
| return true; |
| } |
| const int logInterval = 60; |
| int64_t lastLogged = rt.tv_sec; |
| int sent = 0; |
| int lastSent = 0; |
| bool vsyncEnabled = false; |
| |
| struct timespec wait_time; |
| wait_time.tv_sec = 0; |
| wait_time.tv_nsec = mDisplay.mVsyncPeriod; |
| const int64_t kOneRefreshNs = mDisplay.mVsyncPeriod; |
| const int64_t kOneSecondNs = 1000ULL * 1000ULL * 1000ULL; |
| int64_t lastTimeNs = -1; |
| int64_t phasedWaitNs = 0; |
| int64_t currentNs = 0; |
| |
| while (true) { |
| clock_gettime(CLOCK_MONOTONIC, &rt); |
| currentNs = rt.tv_nsec + rt.tv_sec * kOneSecondNs; |
| |
| if (lastTimeNs < 0) { |
| phasedWaitNs = currentNs + kOneRefreshNs; |
| } else { |
| phasedWaitNs = kOneRefreshNs * |
| (( currentNs - lastTimeNs) / kOneRefreshNs + 1) + |
| lastTimeNs; |
| } |
| |
| wait_time.tv_sec = phasedWaitNs / kOneSecondNs; |
| wait_time.tv_nsec = phasedWaitNs - wait_time.tv_sec * kOneSecondNs; |
| |
| int ret; |
| do { |
| ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &wait_time, NULL); |
| } while (ret == -1 && errno == EINTR); |
| |
| lastTimeNs = phasedWaitNs; |
| |
| std::unique_lock<std::mutex> lock(mDisplay.mStateMutex); |
| vsyncEnabled = (mDisplay.mVsyncEnabled == Vsync::Enable); |
| lock.unlock(); |
| |
| if (!vsyncEnabled) { |
| continue; |
| } |
| |
| lock.lock(); |
| const auto& callbackInfo = mDisplay.mDevice.mCallbacks[Callback::Vsync]; |
| auto vsync = reinterpret_cast<HWC2_PFN_VSYNC>(callbackInfo.pointer); |
| lock.unlock(); |
| |
| if (vsync) { |
| vsync(callbackInfo.data, mDisplay.mId, lastTimeNs); |
| } |
| |
| if (rt.tv_sec - lastLogged >= logInterval) { |
| ALOGVV("sent %d syncs in %ds", sent - lastSent, rt.tv_sec - lastLogged); |
| lastLogged = rt.tv_sec; |
| lastSent = sent; |
| } |
| ++sent; |
| } |
| return false; |
| } |
| |
| |
| // Layer functions |
| bool EmuHWC2::SortLayersByZ::operator()(const std::shared_ptr<Layer>& lhs, |
| const std::shared_ptr<Layer>& rhs) const { |
| return lhs->getZ() < rhs->getZ(); |
| } |
| |
| std::atomic<hwc2_layer_t> EmuHWC2::Layer::sNextId(1); |
| |
| EmuHWC2::Layer::Layer(Display& display) |
| : mId(sNextId++), |
| mDisplay(display), |
| mBuffer(), |
| mSurfaceDamage(), |
| mBlendMode(BlendMode::None), |
| mColor({0, 0, 0, 0}), |
| mCompositionType(Composition::Invalid), |
| mDisplayFrame({0, 0, -1, -1}), |
| mPlaneAlpha(0.0f), |
| mSidebandStream(nullptr), |
| mSourceCrop({0.0f, 0.0f, -1.0f, -1.0f}), |
| mTransform(Transform::None), |
| mVisibleRegion(), |
| mZ(0) |
| {} |
| |
| Error EmuHWC2::Layer::setBuffer(buffer_handle_t buffer, |
| int32_t acquireFence) { |
| ALOGVV("%s: Setting acquireFence %d for layer %u", __FUNCTION__, |
| acquireFence, (uint32_t)mId); |
| mBuffer.setBuffer(buffer); |
| mBuffer.setFence(acquireFence); |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Layer::setCursorPosition(int32_t /*x*/, |
| int32_t /*y*/) { |
| ALOGVV("%s layer %u", __FUNCTION__, (uint32_t)mId); |
| if (mCompositionType != Composition::Cursor) { |
| ALOGE("%s: CompositionType not Cursor type", __FUNCTION__); |
| return Error::BadLayer; |
| } |
| //TODO |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Layer::setSurfaceDamage(hwc_region_t /*damage*/) { |
| // Emulator redraw whole layer per frame, so ignore this. |
| ALOGVV("%s", __FUNCTION__); |
| return Error::None; |
| } |
| |
| // Layer state functions |
| |
| Error EmuHWC2::Layer::setBlendMode(int32_t mode) { |
| ALOGVV("%s %d for layer %u", __FUNCTION__, mode, (uint32_t)mId); |
| mBlendMode = static_cast<BlendMode>(mode); |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Layer::setColor(hwc_color_t color) { |
| ALOGVV("%s layer %u %d", __FUNCTION__, (uint32_t)mId, color); |
| mColor = color; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Layer::setCompositionType(int32_t type) { |
| ALOGVV("%s layer %u %u", __FUNCTION__, (uint32_t)mId, type); |
| mCompositionType = static_cast<Composition>(type); |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Layer::setDataspace(int32_t) { |
| ALOGVV("%s", __FUNCTION__); |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Layer::setDisplayFrame(hwc_rect_t frame) { |
| ALOGVV("%s layer %u", __FUNCTION__, (uint32_t)mId); |
| mDisplayFrame = frame; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Layer::setPlaneAlpha(float alpha) { |
| ALOGVV("%s layer %u %f", __FUNCTION__, (uint32_t)mId, alpha); |
| mPlaneAlpha = alpha; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Layer::setSidebandStream(const native_handle_t* stream) { |
| ALOGVV("%s layer %u", __FUNCTION__, (uint32_t)mId); |
| mSidebandStream = stream; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Layer::setSourceCrop(hwc_frect_t crop) { |
| ALOGVV("%s layer %u", __FUNCTION__, (uint32_t)mId); |
| mSourceCrop = crop; |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Layer::setTransform(int32_t transform) { |
| ALOGVV("%s layer %u", __FUNCTION__, (uint32_t)mId); |
| mTransform = static_cast<Transform>(transform); |
| return Error::None; |
| } |
| |
| static bool compareRects(const hwc_rect_t& rect1, const hwc_rect_t& rect2) { |
| return rect1.left == rect2.left && |
| rect1.right == rect2.right && |
| rect1.top == rect2.top && |
| rect1.bottom == rect2.bottom; |
| } |
| |
| Error EmuHWC2::Layer::setVisibleRegion(hwc_region_t visible) { |
| ALOGVV("%s", __FUNCTION__); |
| if ((getNumVisibleRegions() != visible.numRects) || |
| !std::equal(mVisibleRegion.begin(), mVisibleRegion.end(), visible.rects, |
| compareRects)) { |
| mVisibleRegion.resize(visible.numRects); |
| std::copy_n(visible.rects, visible.numRects, mVisibleRegion.begin()); |
| } |
| return Error::None; |
| } |
| |
| Error EmuHWC2::Layer::setZ(uint32_t z) { |
| ALOGVV("%s layer %u %d", __FUNCTION__, (uint32_t)mId, z); |
| mZ = z; |
| return Error::None; |
| } |
| |
| // Adaptor Helpers |
| |
| void EmuHWC2::populateCapabilities() { |
| //TODO: add Capabilities |
| // support virtualDisplay |
| // support sideBandStream |
| // support backGroundColor |
| // we should not set this for HWC2, TODO: remove |
| // mCapabilities.insert(Capability::PresentFenceIsNotReliable); |
| } |
| |
| int EmuHWC2::populatePrimary() { |
| int ret = 0; |
| auto display = std::make_shared<Display>(*this, HWC2::DisplayType::Physical, |
| mDisplayWidth, mDisplayHeight); |
| ret = display->populatePrimaryConfigs(mDisplayWidth, mDisplayHeight, |
| mDisplayDpiX, mDisplayDpiY); |
| if (ret != 0) { |
| return ret; |
| } |
| mDisplays.emplace(display->getId(), std::move(display)); |
| return ret; |
| } |
| |
| // Note "hwservicemanager." is used to avoid selinux issue |
| #define EXTERANL_DISPLAY_PROP "hwservicemanager.external.displays" |
| |
| // return 0 for successful, 1 if no external displays are specified |
| // return < 0 if failed |
| int EmuHWC2::populateSecondaryDisplays() { |
| // this guest property, hwservicemanager.external.displays, |
| // specifies multi-display info, with comma (,) as separator |
| // each display has the following info: |
| // physicalId,width,height,dpi,flags |
| // serveral displays can be provided, e.g., following has 2 displays: |
| // setprop hwservicemanager.external.displays 1,1200,800,120,0,2,1200,800,120,0 |
| std::vector<uint64_t> values; |
| char displaysValue[PROPERTY_VALUE_MAX] = ""; |
| property_get(EXTERANL_DISPLAY_PROP, displaysValue, ""); |
| bool isValid = displaysValue[0] != '\0'; |
| if (isValid) { |
| char *p = displaysValue; |
| while (*p) { |
| if (!isdigit(*p) && *p != ',' && *p != ' ') { |
| isValid = false; |
| break; |
| } |
| p ++; |
| } |
| if (!isValid) { |
| ALOGE("Invalid syntax for the value of system prop: %s", EXTERANL_DISPLAY_PROP); |
| } |
| } |
| if (!isValid) { |
| // no external displays are specified |
| return 1; |
| } |
| // parse all int values to a vector |
| std::istringstream stream(displaysValue); |
| for (uint64_t id; stream >> id;) { |
| values.push_back(id); |
| if (stream.peek() == ',') |
| stream.ignore(); |
| } |
| // each display has 5 values |
| if ((values.size() % 5) != 0) { |
| ALOGE("%s: invalid value for system property: %s", __FUNCTION__, EXTERANL_DISPLAY_PROP); |
| return -1; |
| } |
| uint32_t idx = 0; |
| while (!values.empty()) { |
| // uint64_t physicalId = values[0]; |
| uint32_t width = values[1]; |
| uint32_t height = values[2]; |
| uint32_t dpi = values[3]; |
| // uint32_t flags = values[4]; |
| values.erase(values.begin(), values.begin() + 5); |
| |
| Error ret = Error::None; |
| auto display = std::make_shared<Display>(*this, HWC2::DisplayType::Physical, width, height); |
| ret = display->populateSecondaryConfigs(width, height, dpi, idx++); |
| if (ret != Error::None) { |
| return -2; |
| } |
| mDisplays.emplace(display->getId(), std::move(display)); |
| } |
| return 0; |
| } |
| |
| EmuHWC2::Display* EmuHWC2::getDisplay(hwc2_display_t id) { |
| auto display = mDisplays.find(id); |
| if (display == mDisplays.end()) { |
| ALOGE("Failed to get display for id=%d", (uint32_t)id); |
| return nullptr; |
| } |
| return display->second.get(); |
| } |
| |
| std::tuple<EmuHWC2::Layer*, Error> EmuHWC2::getLayer( |
| hwc2_display_t displayId, hwc2_layer_t layerId) { |
| auto display = getDisplay(displayId); |
| if (!display) { |
| ALOGE("%s: Fail to find display %d", __FUNCTION__, (uint32_t)displayId); |
| return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadDisplay); |
| } |
| |
| auto layerEntry = mLayers.find(layerId); |
| if (layerEntry == mLayers.end()) { |
| ALOGE("%s: Fail to find layer %d", __FUNCTION__, (uint32_t)layerId); |
| return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer); |
| } |
| |
| auto layer = layerEntry->second; |
| if (layer->getDisplay().getId() != displayId) { |
| ALOGE("%s: layer %d not belongs to display %d", __FUNCTION__, |
| (uint32_t)layerId, (uint32_t)displayId); |
| return std::make_tuple(static_cast<Layer*>(nullptr), Error::BadLayer); |
| } |
| return std::make_tuple(layer.get(), Error::None); |
| } |
| |
| static int hwc2DevOpen(const struct hw_module_t *module, const char *name, |
| struct hw_device_t **dev) { |
| ALOGVV("%s ", __FUNCTION__); |
| if (strcmp(name, HWC_HARDWARE_COMPOSER)) { |
| ALOGE("Invalid module name- %s", name); |
| return -EINVAL; |
| } |
| |
| EmuHWC2* ctx = new EmuHWC2(); |
| if (!ctx) { |
| ALOGE("Failed to allocate EmuHWC2"); |
| return -ENOMEM; |
| } |
| int ret = ctx->populatePrimary(); |
| if (ret != 0) { |
| ALOGE("Failed to populate primary display"); |
| return ret; |
| } |
| |
| ret = ctx->populateSecondaryDisplays(); |
| if (ret < 0) { |
| ALOGE("Failed to populate secondary displays"); |
| return ret; |
| } |
| |
| ctx->common.module = const_cast<hw_module_t *>(module); |
| *dev = &ctx->common; |
| return 0; |
| } |
| } |
| |
| static struct hw_module_methods_t hwc2_module_methods = { |
| .open = android::hwc2DevOpen |
| }; |
| |
| hw_module_t HAL_MODULE_INFO_SYM = { |
| .tag = HARDWARE_MODULE_TAG, |
| .version_major = 2, |
| .version_minor = 0, |
| .id = HWC_HARDWARE_MODULE_ID, |
| .name = "goldfish HWC2 module", |
| .author = "The Android Open Source Project", |
| .methods = &hwc2_module_methods, |
| .dso = NULL, |
| .reserved = {0}, |
| }; |