|  | /* | 
|  | * Copyright 2017 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_TAG "HWC2OnFbAdapter" | 
|  |  | 
|  | //#define LOG_NDEBUG 0 | 
|  |  | 
|  | #include "hwc2onfbadapter/HWC2OnFbAdapter.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <type_traits> | 
|  |  | 
|  | #include <inttypes.h> | 
|  | #include <time.h> | 
|  | #include <sys/prctl.h> | 
|  | #include <unistd.h> // for close | 
|  |  | 
|  | #include <hardware/fb.h> | 
|  | #include <log/log.h> | 
|  | #include <sync/sync.h> | 
|  |  | 
|  | namespace android { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | void dumpHook(hwc2_device_t* device, uint32_t* outSize, char* outBuffer) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (outBuffer) { | 
|  | *outSize = adapter.getDebugString().copy(outBuffer, *outSize); | 
|  | } else { | 
|  | adapter.updateDebugString(); | 
|  | *outSize = adapter.getDebugString().size(); | 
|  | } | 
|  | } | 
|  |  | 
|  | int32_t registerCallbackHook(hwc2_device_t* device, int32_t descriptor, | 
|  | hwc2_callback_data_t callbackData, hwc2_function_pointer_t pointer) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | switch (descriptor) { | 
|  | case HWC2_CALLBACK_HOTPLUG: | 
|  | if (pointer) { | 
|  | reinterpret_cast<HWC2_PFN_HOTPLUG>(pointer)(callbackData, adapter.getDisplayId(), | 
|  | HWC2_CONNECTION_CONNECTED); | 
|  | } | 
|  | break; | 
|  | case HWC2_CALLBACK_REFRESH: | 
|  | break; | 
|  | case HWC2_CALLBACK_VSYNC: | 
|  | adapter.setVsyncCallback(reinterpret_cast<HWC2_PFN_VSYNC>(pointer), callbackData); | 
|  | break; | 
|  | default: | 
|  | return HWC2_ERROR_BAD_PARAMETER; | 
|  | } | 
|  |  | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | uint32_t getMaxVirtualDisplayCountHook(hwc2_device_t* /*device*/) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t createVirtualDisplayHook(hwc2_device_t* /*device*/, uint32_t /*width*/, uint32_t /*height*/, | 
|  | int32_t* /*format*/, hwc2_display_t* /*outDisplay*/) { | 
|  | return HWC2_ERROR_NO_RESOURCES; | 
|  | } | 
|  |  | 
|  | int32_t destroyVirtualDisplayHook(hwc2_device_t* /*device*/, hwc2_display_t /*display*/) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | int32_t setOutputBufferHook(hwc2_device_t* /*device*/, hwc2_display_t /*display*/, | 
|  | buffer_handle_t /*buffer*/, int32_t /*releaseFence*/) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | int32_t getDisplayNameHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outSize, | 
|  | char* outName) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | const auto& info = adapter.getInfo(); | 
|  | if (outName) { | 
|  | *outSize = info.name.copy(outName, *outSize); | 
|  | } else { | 
|  | *outSize = info.name.size(); | 
|  | } | 
|  |  | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t getDisplayTypeHook(hwc2_device_t* device, hwc2_display_t display, int32_t* outType) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | *outType = HWC2_DISPLAY_TYPE_PHYSICAL; | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t getDozeSupportHook(hwc2_device_t* device, hwc2_display_t display, int32_t* outSupport) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | *outSupport = 0; | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t getHdrCapabilitiesHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outNumTypes, | 
|  | int32_t* /*outTypes*/, float* /*outMaxLuminance*/, | 
|  | float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | *outNumTypes = 0; | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t setPowerModeHook(hwc2_device_t* device, hwc2_display_t display, int32_t /*mode*/) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | // pretend that it works | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t setVsyncEnabledHook(hwc2_device_t* device, hwc2_display_t display, int32_t enabled) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | adapter.enableVsync(enabled == HWC2_VSYNC_ENABLE); | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t getColorModesHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outNumModes, | 
|  | int32_t* outModes) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | if (outModes) { | 
|  | if (*outNumModes > 0) { | 
|  | outModes[0] = HAL_COLOR_MODE_NATIVE; | 
|  | *outNumModes = 1; | 
|  | } | 
|  | } else { | 
|  | *outNumModes = 1; | 
|  | } | 
|  |  | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t setColorModeHook(hwc2_device_t* device, hwc2_display_t display, int32_t mode) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (mode != HAL_COLOR_MODE_NATIVE) { | 
|  | return HWC2_ERROR_BAD_PARAMETER; | 
|  | } | 
|  |  | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t setColorTransformHook(hwc2_device_t* device, hwc2_display_t display, | 
|  | const float* /*matrix*/, int32_t /*hint*/) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | // we always force client composition | 
|  | adapter.setState(HWC2OnFbAdapter::State::MODIFIED); | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t getClientTargetSupportHook(hwc2_device_t* device, hwc2_display_t display, uint32_t width, | 
|  | uint32_t height, int32_t format, int32_t dataspace) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (dataspace != HAL_DATASPACE_UNKNOWN) { | 
|  | return HWC2_ERROR_UNSUPPORTED; | 
|  | } | 
|  |  | 
|  | const auto& info = adapter.getInfo(); | 
|  | return (info.width == width && info.height == height && info.format == format) | 
|  | ? HWC2_ERROR_NONE | 
|  | : HWC2_ERROR_UNSUPPORTED; | 
|  | } | 
|  |  | 
|  | int32_t setClientTargetHook(hwc2_device_t* device, hwc2_display_t display, buffer_handle_t target, | 
|  | int32_t acquireFence, int32_t dataspace, hwc_region_t /*damage*/) { | 
|  | if (acquireFence >= 0) { | 
|  | sync_wait(acquireFence, -1); | 
|  | close(acquireFence); | 
|  | } | 
|  |  | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (dataspace != HAL_DATASPACE_UNKNOWN) { | 
|  | return HWC2_ERROR_BAD_PARAMETER; | 
|  | } | 
|  |  | 
|  | // no state change | 
|  | adapter.setBuffer(target); | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t getDisplayConfigsHook(hwc2_device_t* device, hwc2_display_t display, | 
|  | uint32_t* outNumConfigs, hwc2_config_t* outConfigs) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | if (outConfigs) { | 
|  | if (*outNumConfigs > 0) { | 
|  | outConfigs[0] = adapter.getConfigId(); | 
|  | *outNumConfigs = 1; | 
|  | } | 
|  | } else { | 
|  | *outNumConfigs = 1; | 
|  | } | 
|  |  | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t getDisplayAttributeHook(hwc2_device_t* device, hwc2_display_t display, hwc2_config_t config, | 
|  | int32_t attribute, int32_t* outValue) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (adapter.getConfigId() != config) { | 
|  | return HWC2_ERROR_BAD_CONFIG; | 
|  | } | 
|  |  | 
|  | const auto& info = adapter.getInfo(); | 
|  | switch (attribute) { | 
|  | case HWC2_ATTRIBUTE_WIDTH: | 
|  | *outValue = int32_t(info.width); | 
|  | break; | 
|  | case HWC2_ATTRIBUTE_HEIGHT: | 
|  | *outValue = int32_t(info.height); | 
|  | break; | 
|  | case HWC2_ATTRIBUTE_VSYNC_PERIOD: | 
|  | *outValue = int32_t(info.vsync_period_ns); | 
|  | break; | 
|  | case HWC2_ATTRIBUTE_DPI_X: | 
|  | *outValue = int32_t(info.xdpi_scaled); | 
|  | break; | 
|  | case HWC2_ATTRIBUTE_DPI_Y: | 
|  | *outValue = int32_t(info.ydpi_scaled); | 
|  | break; | 
|  | default: | 
|  | return HWC2_ERROR_BAD_PARAMETER; | 
|  | } | 
|  |  | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t getActiveConfigHook(hwc2_device_t* device, hwc2_display_t display, | 
|  | hwc2_config_t* outConfig) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | *outConfig = adapter.getConfigId(); | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t setActiveConfigHook(hwc2_device_t* device, hwc2_display_t display, hwc2_config_t config) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (adapter.getConfigId() != config) { | 
|  | return HWC2_ERROR_BAD_CONFIG; | 
|  | } | 
|  |  | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t validateDisplayHook(hwc2_device_t* device, hwc2_display_t display, uint32_t* outNumTypes, | 
|  | uint32_t* outNumRequests) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | const auto& dirtyLayers = adapter.getDirtyLayers(); | 
|  | *outNumTypes = dirtyLayers.size(); | 
|  | *outNumRequests = 0; | 
|  |  | 
|  | if (*outNumTypes > 0) { | 
|  | adapter.setState(HWC2OnFbAdapter::State::VALIDATED_WITH_CHANGES); | 
|  | return HWC2_ERROR_HAS_CHANGES; | 
|  | } else { | 
|  | adapter.setState(HWC2OnFbAdapter::State::VALIDATED); | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  | } | 
|  |  | 
|  | int32_t getChangedCompositionTypesHook(hwc2_device_t* device, hwc2_display_t display, | 
|  | uint32_t* outNumElements, hwc2_layer_t* outLayers, | 
|  | int32_t* outTypes) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (adapter.getState() == HWC2OnFbAdapter::State::MODIFIED) { | 
|  | return HWC2_ERROR_NOT_VALIDATED; | 
|  | } | 
|  |  | 
|  | // request client composition for all layers | 
|  | const auto& dirtyLayers = adapter.getDirtyLayers(); | 
|  | if (outLayers && outTypes) { | 
|  | *outNumElements = std::min(*outNumElements, uint32_t(dirtyLayers.size())); | 
|  | auto iter = dirtyLayers.cbegin(); | 
|  | for (uint32_t i = 0; i < *outNumElements; i++) { | 
|  | outLayers[i] = *iter++; | 
|  | outTypes[i] = HWC2_COMPOSITION_CLIENT; | 
|  | } | 
|  | } else { | 
|  | *outNumElements = dirtyLayers.size(); | 
|  | } | 
|  |  | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t getDisplayRequestsHook(hwc2_device_t* device, hwc2_display_t display, | 
|  | int32_t* outDisplayRequests, uint32_t* outNumElements, | 
|  | hwc2_layer_t* /*outLayers*/, int32_t* /*outLayerRequests*/) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (adapter.getState() == HWC2OnFbAdapter::State::MODIFIED) { | 
|  | return HWC2_ERROR_NOT_VALIDATED; | 
|  | } | 
|  |  | 
|  | *outDisplayRequests = 0; | 
|  | *outNumElements = 0; | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t acceptDisplayChangesHook(hwc2_device_t* device, hwc2_display_t display) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (adapter.getState() == HWC2OnFbAdapter::State::MODIFIED) { | 
|  | return HWC2_ERROR_NOT_VALIDATED; | 
|  | } | 
|  |  | 
|  | adapter.clearDirtyLayers(); | 
|  | adapter.setState(HWC2OnFbAdapter::State::VALIDATED); | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t presentDisplayHook(hwc2_device_t* device, hwc2_display_t display, | 
|  | int32_t* outPresentFence) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (adapter.getState() != HWC2OnFbAdapter::State::VALIDATED) { | 
|  | return HWC2_ERROR_NOT_VALIDATED; | 
|  | } | 
|  |  | 
|  | adapter.postBuffer(); | 
|  | *outPresentFence = -1; | 
|  |  | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t getReleaseFencesHook(hwc2_device_t* device, hwc2_display_t display, | 
|  | uint32_t* outNumElements, hwc2_layer_t* /*outLayers*/, | 
|  | int32_t* /*outFences*/) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | *outNumElements = 0; | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t createLayerHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t* outLayer) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | *outLayer = adapter.addLayer(); | 
|  | adapter.setState(HWC2OnFbAdapter::State::MODIFIED); | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t destroyLayerHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | if (adapter.removeLayer(layer)) { | 
|  | adapter.setState(HWC2OnFbAdapter::State::MODIFIED); | 
|  | return HWC2_ERROR_NONE; | 
|  | } else { | 
|  | return HWC2_ERROR_BAD_LAYER; | 
|  | } | 
|  | } | 
|  |  | 
|  | int32_t setCursorPositionHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t /*layer*/, | 
|  | int32_t /*x*/, int32_t /*y*/) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  |  | 
|  | // always an error | 
|  | return HWC2_ERROR_BAD_LAYER; | 
|  | } | 
|  |  | 
|  | int32_t setLayerBufferHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, | 
|  | buffer_handle_t /*buffer*/, int32_t acquireFence) { | 
|  | if (acquireFence >= 0) { | 
|  | sync_wait(acquireFence, -1); | 
|  | close(acquireFence); | 
|  | } | 
|  |  | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (!adapter.hasLayer(layer)) { | 
|  | return HWC2_ERROR_BAD_LAYER; | 
|  | } | 
|  |  | 
|  | // no state change | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t setLayerSurfaceDamageHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, | 
|  | hwc_region_t /*damage*/) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (!adapter.hasLayer(layer)) { | 
|  | return HWC2_ERROR_BAD_LAYER; | 
|  | } | 
|  |  | 
|  | // no state change | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | int32_t setLayerCompositionTypeHook(hwc2_device_t* device, hwc2_display_t display, | 
|  | hwc2_layer_t layer, int32_t type) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (!adapter.markLayerDirty(layer, type != HWC2_COMPOSITION_CLIENT)) { | 
|  | return HWC2_ERROR_BAD_LAYER; | 
|  | } | 
|  |  | 
|  | adapter.setState(HWC2OnFbAdapter::State::MODIFIED); | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | template <typename... Args> | 
|  | int32_t setLayerStateHook(hwc2_device_t* device, hwc2_display_t display, hwc2_layer_t layer, | 
|  | Args... /*args*/) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | if (adapter.getDisplayId() != display) { | 
|  | return HWC2_ERROR_BAD_DISPLAY; | 
|  | } | 
|  | if (!adapter.hasLayer(layer)) { | 
|  | return HWC2_ERROR_BAD_LAYER; | 
|  | } | 
|  |  | 
|  | adapter.setState(HWC2OnFbAdapter::State::MODIFIED); | 
|  | return HWC2_ERROR_NONE; | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | hwc2_function_pointer_t getFunctionHook(hwc2_device_t* /*device*/, int32_t descriptor) { | 
|  | switch (descriptor) { | 
|  | // global functions | 
|  | case HWC2_FUNCTION_DUMP: | 
|  | return asFP<HWC2_PFN_DUMP>(dumpHook); | 
|  | case HWC2_FUNCTION_REGISTER_CALLBACK: | 
|  | return asFP<HWC2_PFN_REGISTER_CALLBACK>(registerCallbackHook); | 
|  |  | 
|  | // virtual display functions | 
|  | case HWC2_FUNCTION_GET_MAX_VIRTUAL_DISPLAY_COUNT: | 
|  | return asFP<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>(getMaxVirtualDisplayCountHook); | 
|  | case HWC2_FUNCTION_CREATE_VIRTUAL_DISPLAY: | 
|  | return asFP<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>(createVirtualDisplayHook); | 
|  | case HWC2_FUNCTION_DESTROY_VIRTUAL_DISPLAY: | 
|  | return asFP<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>(destroyVirtualDisplayHook); | 
|  | case HWC2_FUNCTION_SET_OUTPUT_BUFFER: | 
|  | return asFP<HWC2_PFN_SET_OUTPUT_BUFFER>(setOutputBufferHook); | 
|  |  | 
|  | // display functions | 
|  | case HWC2_FUNCTION_GET_DISPLAY_NAME: | 
|  | return asFP<HWC2_PFN_GET_DISPLAY_NAME>(getDisplayNameHook); | 
|  | case HWC2_FUNCTION_GET_DISPLAY_TYPE: | 
|  | return asFP<HWC2_PFN_GET_DISPLAY_TYPE>(getDisplayTypeHook); | 
|  | case HWC2_FUNCTION_GET_DOZE_SUPPORT: | 
|  | return asFP<HWC2_PFN_GET_DOZE_SUPPORT>(getDozeSupportHook); | 
|  | case HWC2_FUNCTION_GET_HDR_CAPABILITIES: | 
|  | return asFP<HWC2_PFN_GET_HDR_CAPABILITIES>(getHdrCapabilitiesHook); | 
|  | case HWC2_FUNCTION_SET_POWER_MODE: | 
|  | return asFP<HWC2_PFN_SET_POWER_MODE>(setPowerModeHook); | 
|  | case HWC2_FUNCTION_SET_VSYNC_ENABLED: | 
|  | return asFP<HWC2_PFN_SET_VSYNC_ENABLED>(setVsyncEnabledHook); | 
|  | case HWC2_FUNCTION_GET_COLOR_MODES: | 
|  | return asFP<HWC2_PFN_GET_COLOR_MODES>(getColorModesHook); | 
|  | case HWC2_FUNCTION_SET_COLOR_MODE: | 
|  | return asFP<HWC2_PFN_SET_COLOR_MODE>(setColorModeHook); | 
|  | case HWC2_FUNCTION_SET_COLOR_TRANSFORM: | 
|  | return asFP<HWC2_PFN_SET_COLOR_TRANSFORM>(setColorTransformHook); | 
|  | case HWC2_FUNCTION_GET_CLIENT_TARGET_SUPPORT: | 
|  | return asFP<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>(getClientTargetSupportHook); | 
|  | case HWC2_FUNCTION_SET_CLIENT_TARGET: | 
|  | return asFP<HWC2_PFN_SET_CLIENT_TARGET>(setClientTargetHook); | 
|  |  | 
|  | // config functions | 
|  | case HWC2_FUNCTION_GET_DISPLAY_CONFIGS: | 
|  | return asFP<HWC2_PFN_GET_DISPLAY_CONFIGS>(getDisplayConfigsHook); | 
|  | case HWC2_FUNCTION_GET_DISPLAY_ATTRIBUTE: | 
|  | return asFP<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>(getDisplayAttributeHook); | 
|  | case HWC2_FUNCTION_GET_ACTIVE_CONFIG: | 
|  | return asFP<HWC2_PFN_GET_ACTIVE_CONFIG>(getActiveConfigHook); | 
|  | case HWC2_FUNCTION_SET_ACTIVE_CONFIG: | 
|  | return asFP<HWC2_PFN_SET_ACTIVE_CONFIG>(setActiveConfigHook); | 
|  |  | 
|  | // validate/present functions | 
|  | case HWC2_FUNCTION_VALIDATE_DISPLAY: | 
|  | return asFP<HWC2_PFN_VALIDATE_DISPLAY>(validateDisplayHook); | 
|  | case HWC2_FUNCTION_GET_CHANGED_COMPOSITION_TYPES: | 
|  | return asFP<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>(getChangedCompositionTypesHook); | 
|  | case HWC2_FUNCTION_GET_DISPLAY_REQUESTS: | 
|  | return asFP<HWC2_PFN_GET_DISPLAY_REQUESTS>(getDisplayRequestsHook); | 
|  | case HWC2_FUNCTION_ACCEPT_DISPLAY_CHANGES: | 
|  | return asFP<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>(acceptDisplayChangesHook); | 
|  | case HWC2_FUNCTION_PRESENT_DISPLAY: | 
|  | return asFP<HWC2_PFN_PRESENT_DISPLAY>(presentDisplayHook); | 
|  | case HWC2_FUNCTION_GET_RELEASE_FENCES: | 
|  | return asFP<HWC2_PFN_GET_RELEASE_FENCES>(getReleaseFencesHook); | 
|  |  | 
|  | // layer create/destroy | 
|  | case HWC2_FUNCTION_CREATE_LAYER: | 
|  | return asFP<HWC2_PFN_CREATE_LAYER>(createLayerHook); | 
|  | case HWC2_FUNCTION_DESTROY_LAYER: | 
|  | return asFP<HWC2_PFN_DESTROY_LAYER>(destroyLayerHook); | 
|  |  | 
|  | // layer functions; validateDisplay not required | 
|  | case HWC2_FUNCTION_SET_CURSOR_POSITION: | 
|  | return asFP<HWC2_PFN_SET_CURSOR_POSITION>(setCursorPositionHook); | 
|  | case HWC2_FUNCTION_SET_LAYER_BUFFER: | 
|  | return asFP<HWC2_PFN_SET_LAYER_BUFFER>(setLayerBufferHook); | 
|  | case HWC2_FUNCTION_SET_LAYER_SURFACE_DAMAGE: | 
|  | return asFP<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>(setLayerSurfaceDamageHook); | 
|  |  | 
|  | // layer state functions; validateDisplay required | 
|  | case HWC2_FUNCTION_SET_LAYER_COMPOSITION_TYPE: | 
|  | return asFP<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>(setLayerCompositionTypeHook); | 
|  | case HWC2_FUNCTION_SET_LAYER_BLEND_MODE: | 
|  | return asFP<HWC2_PFN_SET_LAYER_BLEND_MODE>(setLayerStateHook<int32_t>); | 
|  | case HWC2_FUNCTION_SET_LAYER_COLOR: | 
|  | return asFP<HWC2_PFN_SET_LAYER_COLOR>(setLayerStateHook<hwc_color_t>); | 
|  | case HWC2_FUNCTION_SET_LAYER_DATASPACE: | 
|  | return asFP<HWC2_PFN_SET_LAYER_DATASPACE>(setLayerStateHook<int32_t>); | 
|  | case HWC2_FUNCTION_SET_LAYER_DISPLAY_FRAME: | 
|  | return asFP<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>(setLayerStateHook<hwc_rect_t>); | 
|  | case HWC2_FUNCTION_SET_LAYER_PLANE_ALPHA: | 
|  | return asFP<HWC2_PFN_SET_LAYER_PLANE_ALPHA>(setLayerStateHook<float>); | 
|  | case HWC2_FUNCTION_SET_LAYER_SIDEBAND_STREAM: | 
|  | return asFP<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>(setLayerStateHook<buffer_handle_t>); | 
|  | case HWC2_FUNCTION_SET_LAYER_SOURCE_CROP: | 
|  | return asFP<HWC2_PFN_SET_LAYER_SOURCE_CROP>(setLayerStateHook<hwc_frect_t>); | 
|  | case HWC2_FUNCTION_SET_LAYER_TRANSFORM: | 
|  | return asFP<HWC2_PFN_SET_LAYER_TRANSFORM>(setLayerStateHook<int32_t>); | 
|  | case HWC2_FUNCTION_SET_LAYER_VISIBLE_REGION: | 
|  | return asFP<HWC2_PFN_SET_LAYER_VISIBLE_REGION>(setLayerStateHook<hwc_region_t>); | 
|  | case HWC2_FUNCTION_SET_LAYER_Z_ORDER: | 
|  | return asFP<HWC2_PFN_SET_LAYER_Z_ORDER>(setLayerStateHook<uint32_t>); | 
|  |  | 
|  | default: | 
|  | ALOGE("unknown function descriptor %d", descriptor); | 
|  | return nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | void getCapabilitiesHook(hwc2_device_t* /*device*/, uint32_t* outCount, | 
|  | int32_t* /*outCapabilities*/) { | 
|  | *outCount = 0; | 
|  | } | 
|  |  | 
|  | int closeHook(hw_device_t* device) { | 
|  | auto& adapter = HWC2OnFbAdapter::cast(device); | 
|  | adapter.close(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | } // anonymous namespace | 
|  |  | 
|  | HWC2OnFbAdapter::HWC2OnFbAdapter(framebuffer_device_t* fbDevice) | 
|  | : hwc2_device_t(), mFbDevice(fbDevice) { | 
|  | common.close = closeHook; | 
|  | hwc2_device::getCapabilities = getCapabilitiesHook; | 
|  | hwc2_device::getFunction = getFunctionHook; | 
|  |  | 
|  | mFbInfo.name = "fbdev"; | 
|  | mFbInfo.width = mFbDevice->width; | 
|  | mFbInfo.height = mFbDevice->height; | 
|  | mFbInfo.format = mFbDevice->format; | 
|  | mFbInfo.vsync_period_ns = int(1e9 / mFbDevice->fps); | 
|  | mFbInfo.xdpi_scaled = int(mFbDevice->xdpi * 1000.0f); | 
|  | mFbInfo.ydpi_scaled = int(mFbDevice->ydpi * 1000.0f); | 
|  |  | 
|  | mVsyncThread.start(0, mFbInfo.vsync_period_ns); | 
|  | } | 
|  |  | 
|  | HWC2OnFbAdapter& HWC2OnFbAdapter::cast(hw_device_t* device) { | 
|  | return *reinterpret_cast<HWC2OnFbAdapter*>(device); | 
|  | } | 
|  |  | 
|  | HWC2OnFbAdapter& HWC2OnFbAdapter::cast(hwc2_device_t* device) { | 
|  | return *reinterpret_cast<HWC2OnFbAdapter*>(device); | 
|  | } | 
|  |  | 
|  | hwc2_display_t HWC2OnFbAdapter::getDisplayId() { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | hwc2_config_t HWC2OnFbAdapter::getConfigId() { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void HWC2OnFbAdapter::close() { | 
|  | mVsyncThread.stop(); | 
|  | framebuffer_close(mFbDevice); | 
|  | } | 
|  |  | 
|  | const HWC2OnFbAdapter::Info& HWC2OnFbAdapter::getInfo() const { | 
|  | return mFbInfo; | 
|  | } | 
|  |  | 
|  | void HWC2OnFbAdapter::updateDebugString() { | 
|  | if (mFbDevice->common.version >= 1 && mFbDevice->dump) { | 
|  | char buffer[4096]; | 
|  | mFbDevice->dump(mFbDevice, buffer, sizeof(buffer)); | 
|  | buffer[sizeof(buffer) - 1] = '\0'; | 
|  |  | 
|  | mDebugString = buffer; | 
|  | } | 
|  | } | 
|  |  | 
|  | const std::string& HWC2OnFbAdapter::getDebugString() const { | 
|  | return mDebugString; | 
|  | } | 
|  |  | 
|  | void HWC2OnFbAdapter::setState(State state) { | 
|  | mState = state; | 
|  | } | 
|  |  | 
|  | HWC2OnFbAdapter::State HWC2OnFbAdapter::getState() const { | 
|  | return mState; | 
|  | } | 
|  |  | 
|  | hwc2_layer_t HWC2OnFbAdapter::addLayer() { | 
|  | hwc2_layer_t id = ++mNextLayerId; | 
|  |  | 
|  | mLayers.insert(id); | 
|  | mDirtyLayers.insert(id); | 
|  |  | 
|  | return id; | 
|  | } | 
|  |  | 
|  | bool HWC2OnFbAdapter::removeLayer(hwc2_layer_t layer) { | 
|  | mDirtyLayers.erase(layer); | 
|  | return mLayers.erase(layer); | 
|  | } | 
|  |  | 
|  | bool HWC2OnFbAdapter::hasLayer(hwc2_layer_t layer) const { | 
|  | return mLayers.count(layer) > 0; | 
|  | } | 
|  |  | 
|  | bool HWC2OnFbAdapter::markLayerDirty(hwc2_layer_t layer, bool dirty) { | 
|  | if (mLayers.count(layer) == 0) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (dirty) { | 
|  | mDirtyLayers.insert(layer); | 
|  | } else { | 
|  | mDirtyLayers.erase(layer); | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | const std::unordered_set<hwc2_layer_t>& HWC2OnFbAdapter::getDirtyLayers() const { | 
|  | return mDirtyLayers; | 
|  | } | 
|  |  | 
|  | void HWC2OnFbAdapter::clearDirtyLayers() { | 
|  | mDirtyLayers.clear(); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * For each frame, SurfaceFlinger | 
|  | * | 
|  | *  - peforms GLES composition | 
|  | *  - calls eglSwapBuffers | 
|  | *  - calls setClientTarget, which maps to setBuffer below | 
|  | *  - calls presentDisplay, which maps to postBuffer below | 
|  | * | 
|  | * setBuffer should be a good place to call compositionComplete. | 
|  | * | 
|  | * As for post, it | 
|  | * | 
|  | *  - schedules the buffer for presentation on the next vsync | 
|  | *  - locks the buffer and blocks all other users trying to lock it | 
|  | * | 
|  | * It does not give us a way to return a present fence, and we need to live | 
|  | * with that.  The implication is that, when we are double-buffered, | 
|  | * SurfaceFlinger assumes the front buffer is available for rendering again | 
|  | * immediately after the back buffer is posted.  The locking semantics | 
|  | * hopefully are strong enough that the rendering will be blocked. | 
|  | */ | 
|  | void HWC2OnFbAdapter::setBuffer(buffer_handle_t buffer) { | 
|  | if (mFbDevice->compositionComplete) { | 
|  | mFbDevice->compositionComplete(mFbDevice); | 
|  | } | 
|  | mBuffer = buffer; | 
|  | } | 
|  |  | 
|  | bool HWC2OnFbAdapter::postBuffer() { | 
|  | int error = 0; | 
|  | if (mBuffer) { | 
|  | error = mFbDevice->post(mFbDevice, mBuffer); | 
|  | } | 
|  |  | 
|  | return error == 0; | 
|  | } | 
|  |  | 
|  | void HWC2OnFbAdapter::setVsyncCallback(HWC2_PFN_VSYNC callback, hwc2_callback_data_t data) { | 
|  | mVsyncThread.setCallback(callback, data); | 
|  | } | 
|  |  | 
|  | void HWC2OnFbAdapter::enableVsync(bool enable) { | 
|  | mVsyncThread.enableCallback(enable); | 
|  | } | 
|  |  | 
|  | int64_t HWC2OnFbAdapter::VsyncThread::now() { | 
|  | struct timespec ts; | 
|  | clock_gettime(CLOCK_MONOTONIC, &ts); | 
|  |  | 
|  | return int64_t(ts.tv_sec) * 1'000'000'000 + ts.tv_nsec; | 
|  | } | 
|  |  | 
|  | bool HWC2OnFbAdapter::VsyncThread::sleepUntil(int64_t t) { | 
|  | struct timespec ts; | 
|  | ts.tv_sec = t / 1'000'000'000; | 
|  | ts.tv_nsec = t % 1'000'000'000; | 
|  |  | 
|  | while (true) { | 
|  | int error = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr); | 
|  | if (error) { | 
|  | if (error == EINTR) { | 
|  | continue; | 
|  | } | 
|  | return false; | 
|  | } else { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void HWC2OnFbAdapter::VsyncThread::start(int64_t firstVsync, int64_t period) { | 
|  | mNextVsync = firstVsync; | 
|  | mPeriod = period; | 
|  | mStarted = true; | 
|  | mThread = std::thread(&VsyncThread::vsyncLoop, this); | 
|  | } | 
|  |  | 
|  | void HWC2OnFbAdapter::VsyncThread::stop() { | 
|  | { | 
|  | std::lock_guard<std::mutex> lock(mMutex); | 
|  | mStarted = false; | 
|  | } | 
|  | mCondition.notify_all(); | 
|  | mThread.join(); | 
|  | } | 
|  |  | 
|  | void HWC2OnFbAdapter::VsyncThread::setCallback(HWC2_PFN_VSYNC callback, hwc2_callback_data_t data) { | 
|  | std::lock_guard<std::mutex> lock(mMutex); | 
|  | mCallback = callback; | 
|  | mCallbackData = data; | 
|  | } | 
|  |  | 
|  | void HWC2OnFbAdapter::VsyncThread::enableCallback(bool enable) { | 
|  | { | 
|  | std::lock_guard<std::mutex> lock(mMutex); | 
|  | mCallbackEnabled = enable; | 
|  | } | 
|  | mCondition.notify_all(); | 
|  | } | 
|  |  | 
|  | void HWC2OnFbAdapter::VsyncThread::vsyncLoop() { | 
|  | prctl(PR_SET_NAME, "VsyncThread", 0, 0, 0); | 
|  |  | 
|  | std::unique_lock<std::mutex> lock(mMutex); | 
|  | if (!mStarted) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | while (true) { | 
|  | if (!mCallbackEnabled) { | 
|  | mCondition.wait(lock, [this] { return mCallbackEnabled || !mStarted; }); | 
|  | if (!mStarted) { | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | lock.unlock(); | 
|  |  | 
|  | // adjust mNextVsync if necessary | 
|  | int64_t t = now(); | 
|  | if (mNextVsync < t) { | 
|  | int64_t n = (t - mNextVsync + mPeriod - 1) / mPeriod; | 
|  | mNextVsync += mPeriod * n; | 
|  | } | 
|  | bool fire = sleepUntil(mNextVsync); | 
|  |  | 
|  | lock.lock(); | 
|  |  | 
|  | if (fire) { | 
|  | ALOGV("VsyncThread(%" PRId64 ")", mNextVsync); | 
|  | if (mCallback) { | 
|  | mCallback(mCallbackData, getDisplayId(), mNextVsync); | 
|  | } | 
|  | mNextVsync += mPeriod; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | } // namespace android |