| /* |
| * 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 |