| /* |
| * Copyright (C) 2016 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 ATRACE_TAG ATRACE_TAG_GRAPHICS |
| #define LOG_TAG "hwc-drm-two" |
| |
| #include "DrmHwcTwo.h" |
| |
| #include <hardware/hardware.h> |
| #include <hardware/hwcomposer2.h> |
| #include <sync/sync.h> |
| |
| #include <cinttypes> |
| #include <iostream> |
| #include <sstream> |
| #include <string> |
| |
| #include "backend/BackendManager.h" |
| #include "bufferinfo/BufferInfoGetter.h" |
| #include "compositor/DrmDisplayComposition.h" |
| #include "utils/log.h" |
| #include "utils/properties.h" |
| |
| namespace android { |
| |
| DrmHwcTwo::DrmHwcTwo() : hwc2_device() { |
| common.tag = HARDWARE_DEVICE_TAG; |
| common.version = HWC_DEVICE_API_VERSION_2_0; |
| common.close = HookDevClose; |
| getCapabilities = HookDevGetCapabilities; |
| getFunction = HookDevGetFunction; |
| } |
| |
| HWC2::Error DrmHwcTwo::CreateDisplay(hwc2_display_t displ, |
| HWC2::DisplayType type) { |
| DrmDevice *drm = resource_manager_.GetDrmDevice(displ); |
| if (!drm) { |
| ALOGE("Failed to get a valid drmresource"); |
| return HWC2::Error::NoResources; |
| } |
| displays_.emplace(std::piecewise_construct, std::forward_as_tuple(displ), |
| std::forward_as_tuple(&resource_manager_, drm, displ, |
| type)); |
| |
| DrmCrtc *crtc = drm->GetCrtcForDisplay(static_cast<int>(displ)); |
| if (!crtc) { |
| ALOGE("Failed to get crtc for display %d", static_cast<int>(displ)); |
| return HWC2::Error::BadDisplay; |
| } |
| auto display_planes = std::vector<DrmPlane *>(); |
| for (const auto &plane : drm->planes()) { |
| if (plane->GetCrtcSupported(*crtc)) |
| display_planes.push_back(plane.get()); |
| } |
| displays_.at(displ).Init(&display_planes); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::Init() { |
| int rv = resource_manager_.Init(); |
| if (rv) { |
| ALOGE("Can't initialize the resource manager %d", rv); |
| return HWC2::Error::NoResources; |
| } |
| |
| HWC2::Error ret = HWC2::Error::None; |
| for (int i = 0; i < resource_manager_.getDisplayCount(); i++) { |
| ret = CreateDisplay(i, HWC2::DisplayType::Physical); |
| if (ret != HWC2::Error::None) { |
| ALOGE("Failed to create display %d with error %d", i, ret); |
| return ret; |
| } |
| } |
| |
| const auto &drm_devices = resource_manager_.getDrmDevices(); |
| for (const auto &device : drm_devices) { |
| // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) |
| device->RegisterHotplugHandler(new DrmHotplugHandler(this, device.get())); |
| } |
| return ret; |
| } |
| |
| template <typename... Args> |
| static inline HWC2::Error unsupported(char const *func, Args... /*args*/) { |
| ALOGV("Unsupported function: %s", func); |
| return HWC2::Error::Unsupported; |
| } |
| |
| static inline void supported(char const *func) { |
| ALOGV("Supported function: %s", func); |
| } |
| |
| HWC2::Error DrmHwcTwo::CreateVirtualDisplay(uint32_t width, uint32_t height, |
| int32_t *format, |
| hwc2_display_t *display) { |
| // TODO(nobody): Implement virtual display |
| return unsupported(__func__, width, height, format, display); |
| } |
| |
| HWC2::Error DrmHwcTwo::DestroyVirtualDisplay(hwc2_display_t display) { |
| // TODO(nobody): Implement virtual display |
| return unsupported(__func__, display); |
| } |
| |
| std::string DrmHwcTwo::HwcDisplay::DumpDelta( |
| DrmHwcTwo::HwcDisplay::Stats delta) { |
| if (delta.total_pixops_ == 0) |
| return "No stats yet"; |
| double ratio = 1.0 - double(delta.gpu_pixops_) / double(delta.total_pixops_); |
| |
| std::stringstream ss; |
| ss << " Total frames count: " << delta.total_frames_ << "\n" |
| << " Failed to test commit frames: " << delta.failed_kms_validate_ << "\n" |
| << " Failed to commit frames: " << delta.failed_kms_present_ << "\n" |
| << ((delta.failed_kms_present_ > 0) |
| ? " !!! Internal failure, FIX it please\n" |
| : "") |
| << " Flattened frames: " << delta.frames_flattened_ << "\n" |
| << " Pixel operations (free units)" |
| << " : [TOTAL: " << delta.total_pixops_ << " / GPU: " << delta.gpu_pixops_ |
| << "]\n" |
| << " Composition efficiency: " << ratio; |
| |
| return ss.str(); |
| } |
| |
| std::string DrmHwcTwo::HwcDisplay::Dump() { |
| std::stringstream ss; |
| ss << "- Display on: " << connector_->name() << "\n" |
| << " Flattening state: " << compositor_.GetFlatteningState() << "\n" |
| << "Statistics since system boot:\n" |
| << DumpDelta(total_stats_) << "\n\n" |
| << "Statistics since last dumpsys request:\n" |
| << DumpDelta(total_stats_.minus(prev_stats_)) << "\n\n"; |
| |
| memcpy(&prev_stats_, &total_stats_, sizeof(Stats)); |
| return ss.str(); |
| } |
| |
| void DrmHwcTwo::Dump(uint32_t *outSize, char *outBuffer) { |
| supported(__func__); |
| |
| if (outBuffer != nullptr) { |
| auto copied_bytes = mDumpString.copy(outBuffer, *outSize); |
| *outSize = static_cast<uint32_t>(copied_bytes); |
| return; |
| } |
| |
| std::stringstream output; |
| |
| output << "-- drm_hwcomposer --\n\n"; |
| |
| for (std::pair<const hwc2_display_t, DrmHwcTwo::HwcDisplay> &dp : displays_) |
| output << dp.second.Dump(); |
| |
| mDumpString = output.str(); |
| *outSize = static_cast<uint32_t>(mDumpString.size()); |
| } |
| |
| uint32_t DrmHwcTwo::GetMaxVirtualDisplayCount() { |
| // TODO(nobody): Implement virtual display |
| unsupported(__func__); |
| return 0; |
| } |
| |
| HWC2::Error DrmHwcTwo::RegisterCallback(int32_t descriptor, |
| hwc2_callback_data_t data, |
| hwc2_function_pointer_t function) { |
| supported(__func__); |
| |
| switch (static_cast<HWC2::Callback>(descriptor)) { |
| case HWC2::Callback::Hotplug: { |
| SetHotplugCallback(data, function); |
| const auto &drm_devices = resource_manager_.getDrmDevices(); |
| for (const auto &device : drm_devices) |
| HandleInitialHotplugState(device.get()); |
| break; |
| } |
| case HWC2::Callback::Refresh: { |
| for (std::pair<const hwc2_display_t, DrmHwcTwo::HwcDisplay> &d : |
| displays_) |
| d.second.RegisterRefreshCallback(data, function); |
| break; |
| } |
| case HWC2::Callback::Vsync: { |
| for (std::pair<const hwc2_display_t, DrmHwcTwo::HwcDisplay> &d : |
| displays_) |
| d.second.RegisterVsyncCallback(data, function); |
| break; |
| } |
| default: |
| break; |
| } |
| return HWC2::Error::None; |
| } |
| |
| DrmHwcTwo::HwcDisplay::HwcDisplay(ResourceManager *resource_manager, |
| DrmDevice *drm, hwc2_display_t handle, |
| HWC2::DisplayType type) |
| : resource_manager_(resource_manager), |
| drm_(drm), |
| handle_(handle), |
| type_(type), |
| color_transform_hint_(HAL_COLOR_TRANSFORM_IDENTITY) { |
| supported(__func__); |
| |
| // clang-format off |
| color_transform_matrix_ = {1.0, 0.0, 0.0, 0.0, |
| 0.0, 1.0, 0.0, 0.0, |
| 0.0, 0.0, 1.0, 0.0, |
| 0.0, 0.0, 0.0, 1.0}; |
| // clang-format on |
| } |
| |
| void DrmHwcTwo::HwcDisplay::ClearDisplay() { |
| compositor_.ClearDisplay(); |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::Init(std::vector<DrmPlane *> *planes) { |
| supported(__func__); |
| planner_ = Planner::CreateInstance(drm_); |
| if (!planner_) { |
| ALOGE("Failed to create planner instance for composition"); |
| return HWC2::Error::NoResources; |
| } |
| |
| int display = static_cast<int>(handle_); |
| int ret = compositor_.Init(resource_manager_, display); |
| if (ret) { |
| ALOGE("Failed display compositor init for display %d (%d)", display, ret); |
| return HWC2::Error::NoResources; |
| } |
| |
| // Split up the given display planes into primary and overlay to properly |
| // interface with the composition |
| char use_overlay_planes_prop[PROPERTY_VALUE_MAX]; |
| property_get("vendor.hwc.drm.use_overlay_planes", use_overlay_planes_prop, |
| "1"); |
| bool use_overlay_planes = strtol(use_overlay_planes_prop, nullptr, 10); |
| for (auto &plane : *planes) { |
| if (plane->type() == DRM_PLANE_TYPE_PRIMARY) |
| primary_planes_.push_back(plane); |
| else if (use_overlay_planes && (plane)->type() == DRM_PLANE_TYPE_OVERLAY) |
| overlay_planes_.push_back(plane); |
| } |
| |
| crtc_ = drm_->GetCrtcForDisplay(display); |
| if (!crtc_) { |
| ALOGE("Failed to get crtc for display %d", display); |
| return HWC2::Error::BadDisplay; |
| } |
| |
| connector_ = drm_->GetConnectorForDisplay(display); |
| if (!connector_) { |
| ALOGE("Failed to get connector for display %d", display); |
| return HWC2::Error::BadDisplay; |
| } |
| |
| ret = vsync_worker_.Init(drm_, display); |
| if (ret) { |
| ALOGE("Failed to create event worker for d=%d %d\n", display, ret); |
| return HWC2::Error::BadDisplay; |
| } |
| |
| ret = BackendManager::GetInstance().SetBackendForDisplay(this); |
| if (ret) { |
| ALOGE("Failed to set backend for d=%d %d\n", display, ret); |
| return HWC2::Error::BadDisplay; |
| } |
| |
| return ChosePreferredConfig(); |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::ChosePreferredConfig() { |
| // Fetch the number of modes from the display |
| uint32_t num_configs = 0; |
| HWC2::Error err = GetDisplayConfigs(&num_configs, nullptr); |
| if (err != HWC2::Error::None || !num_configs) |
| return err; |
| |
| return SetActiveConfig(connector_->get_preferred_mode_id()); |
| } |
| |
| void DrmHwcTwo::HwcDisplay::RegisterVsyncCallback( |
| hwc2_callback_data_t data, hwc2_function_pointer_t func) { |
| supported(__func__); |
| vsync_worker_.RegisterClientCallback(data, func); |
| } |
| |
| void DrmHwcTwo::HwcDisplay::RegisterRefreshCallback( |
| hwc2_callback_data_t data, hwc2_function_pointer_t func) { |
| supported(__func__); |
| compositor_.SetRefreshCallback(data, func); |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::AcceptDisplayChanges() { |
| supported(__func__); |
| for (std::pair<const hwc2_layer_t, DrmHwcTwo::HwcLayer> &l : layers_) |
| l.second.accept_type_change(); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::CreateLayer(hwc2_layer_t *layer) { |
| supported(__func__); |
| layers_.emplace(static_cast<hwc2_layer_t>(layer_idx_), HwcLayer()); |
| *layer = static_cast<hwc2_layer_t>(layer_idx_); |
| ++layer_idx_; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::DestroyLayer(hwc2_layer_t layer) { |
| supported(__func__); |
| if (!get_layer(layer)) |
| return HWC2::Error::BadLayer; |
| |
| layers_.erase(layer); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetActiveConfig(hwc2_config_t *config) { |
| supported(__func__); |
| DrmMode const &mode = connector_->active_mode(); |
| if (mode.id() == 0) |
| return HWC2::Error::BadConfig; |
| |
| *config = mode.id(); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetChangedCompositionTypes( |
| uint32_t *num_elements, hwc2_layer_t *layers, int32_t *types) { |
| supported(__func__); |
| uint32_t num_changes = 0; |
| for (std::pair<const hwc2_layer_t, DrmHwcTwo::HwcLayer> &l : layers_) { |
| if (l.second.type_changed()) { |
| if (layers && num_changes < *num_elements) |
| layers[num_changes] = l.first; |
| if (types && num_changes < *num_elements) |
| types[num_changes] = static_cast<int32_t>(l.second.validated_type()); |
| ++num_changes; |
| } |
| } |
| if (!layers && !types) |
| *num_elements = num_changes; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetClientTargetSupport(uint32_t width, |
| uint32_t height, |
| int32_t /*format*/, |
| int32_t dataspace) { |
| supported(__func__); |
| std::pair<uint32_t, uint32_t> min = drm_->min_resolution(); |
| std::pair<uint32_t, uint32_t> max = drm_->max_resolution(); |
| |
| if (width < min.first || height < min.second) |
| return HWC2::Error::Unsupported; |
| |
| if (width > max.first || height > max.second) |
| return HWC2::Error::Unsupported; |
| |
| if (dataspace != HAL_DATASPACE_UNKNOWN) |
| return HWC2::Error::Unsupported; |
| |
| // TODO(nobody): Validate format can be handled by either GL or planes |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetColorModes(uint32_t *num_modes, |
| int32_t *modes) { |
| supported(__func__); |
| if (!modes) |
| *num_modes = 1; |
| |
| if (modes) |
| *modes = HAL_COLOR_MODE_NATIVE; |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayAttribute(hwc2_config_t config, |
| int32_t attribute_in, |
| int32_t *value) { |
| supported(__func__); |
| auto mode = std::find_if(connector_->modes().begin(), |
| connector_->modes().end(), |
| [config](DrmMode const &m) { |
| return m.id() == config; |
| }); |
| if (mode == connector_->modes().end()) { |
| ALOGE("Could not find active mode for %d", config); |
| return HWC2::Error::BadConfig; |
| } |
| |
| static const int32_t kUmPerInch = 25400; |
| uint32_t mm_width = connector_->mm_width(); |
| uint32_t mm_height = connector_->mm_height(); |
| auto attribute = static_cast<HWC2::Attribute>(attribute_in); |
| switch (attribute) { |
| case HWC2::Attribute::Width: |
| *value = mode->h_display(); |
| break; |
| case HWC2::Attribute::Height: |
| *value = mode->v_display(); |
| break; |
| case HWC2::Attribute::VsyncPeriod: |
| // in nanoseconds |
| *value = 1000.0 * 1000.0 * 1000.0 / mode->v_refresh(); |
| break; |
| case HWC2::Attribute::DpiX: |
| // Dots per 1000 inches |
| *value = mm_width ? (mode->h_display() * kUmPerInch) / mm_width : -1; |
| break; |
| case HWC2::Attribute::DpiY: |
| // Dots per 1000 inches |
| *value = mm_height ? (mode->v_display() * kUmPerInch) / mm_height : -1; |
| break; |
| #if PLATFORM_SDK_VERSION > 29 |
| case HWC2::Attribute::ConfigGroup: |
| *value = 0; /* TODO: Add support for config groups */ |
| break; |
| #endif |
| default: |
| *value = -1; |
| return HWC2::Error::BadConfig; |
| } |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayConfigs(uint32_t *num_configs, |
| hwc2_config_t *configs) { |
| supported(__func__); |
| // Since this callback is normally invoked twice (once to get the count, and |
| // once to populate configs), we don't really want to read the edid |
| // redundantly. Instead, only update the modes on the first invocation. While |
| // it's possible this will result in stale modes, it'll all come out in the |
| // wash when we try to set the active config later. |
| if (!configs) { |
| int ret = connector_->UpdateModes(); |
| if (ret) { |
| ALOGE("Failed to update display modes %d", ret); |
| return HWC2::Error::BadDisplay; |
| } |
| } |
| |
| // Since the upper layers only look at vactive/hactive/refresh, height and |
| // width, it doesn't differentiate interlaced from progressive and other |
| // similar modes. Depending on the order of modes we return to SF, it could |
| // end up choosing a suboptimal configuration and dropping the preferred |
| // mode. To workaround this, don't offer interlaced modes to SF if there is |
| // at least one non-interlaced alternative and only offer a single WxH@R |
| // mode with at least the prefered mode from in DrmConnector::UpdateModes() |
| |
| // TODO(nobody): Remove the following block of code until AOSP handles all |
| // modes |
| std::vector<DrmMode> sel_modes; |
| |
| // Add the preferred mode first to be sure it's not dropped |
| auto mode = std::find_if(connector_->modes().begin(), |
| connector_->modes().end(), [&](DrmMode const &m) { |
| return m.id() == |
| connector_->get_preferred_mode_id(); |
| }); |
| if (mode != connector_->modes().end()) |
| sel_modes.push_back(*mode); |
| |
| // Add the active mode if different from preferred mode |
| if (connector_->active_mode().id() != connector_->get_preferred_mode_id()) |
| sel_modes.push_back(connector_->active_mode()); |
| |
| // Cycle over the modes and filter out "similar" modes, keeping only the |
| // first ones in the order given by DRM (from CEA ids and timings order) |
| for (const DrmMode &mode : connector_->modes()) { |
| // TODO(nobody): Remove this when 3D Attributes are in AOSP |
| if (mode.flags() & DRM_MODE_FLAG_3D_MASK) |
| continue; |
| |
| // TODO(nobody): Remove this when the Interlaced attribute is in AOSP |
| if (mode.flags() & DRM_MODE_FLAG_INTERLACE) { |
| auto m = std::find_if(connector_->modes().begin(), |
| connector_->modes().end(), |
| [&mode](DrmMode const &m) { |
| return !(m.flags() & DRM_MODE_FLAG_INTERLACE) && |
| m.h_display() == mode.h_display() && |
| m.v_display() == mode.v_display(); |
| }); |
| if (m == connector_->modes().end()) |
| sel_modes.push_back(mode); |
| |
| continue; |
| } |
| |
| // Search for a similar WxH@R mode in the filtered list and drop it if |
| // another mode with the same WxH@R has already been selected |
| // TODO(nobody): Remove this when AOSP handles duplicates modes |
| auto m = std::find_if(sel_modes.begin(), sel_modes.end(), |
| [&mode](DrmMode const &m) { |
| return m.h_display() == mode.h_display() && |
| m.v_display() == mode.v_display() && |
| m.v_refresh() == mode.v_refresh(); |
| }); |
| if (m == sel_modes.end()) |
| sel_modes.push_back(mode); |
| } |
| |
| auto num_modes = static_cast<uint32_t>(sel_modes.size()); |
| if (!configs) { |
| *num_configs = num_modes; |
| return HWC2::Error::None; |
| } |
| |
| uint32_t idx = 0; |
| for (const DrmMode &mode : sel_modes) { |
| if (idx >= *num_configs) |
| break; |
| configs[idx++] = mode.id(); |
| } |
| *num_configs = idx; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayName(uint32_t *size, char *name) { |
| supported(__func__); |
| std::ostringstream stream; |
| stream << "display-" << connector_->id(); |
| std::string string = stream.str(); |
| size_t length = string.length(); |
| if (!name) { |
| *size = length; |
| return HWC2::Error::None; |
| } |
| |
| *size = std::min<uint32_t>(static_cast<uint32_t>(length - 1), *size); |
| strncpy(name, string.c_str(), *size); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayRequests(int32_t *display_requests, |
| uint32_t *num_elements, |
| hwc2_layer_t *layers, |
| int32_t *layer_requests) { |
| supported(__func__); |
| // TODO(nobody): I think virtual display should request |
| // HWC2_DISPLAY_REQUEST_WRITE_CLIENT_TARGET_TO_OUTPUT here |
| unsupported(__func__, display_requests, num_elements, layers, layer_requests); |
| *num_elements = 0; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayType(int32_t *type) { |
| supported(__func__); |
| *type = static_cast<int32_t>(type_); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetDozeSupport(int32_t *support) { |
| supported(__func__); |
| *support = 0; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetHdrCapabilities( |
| uint32_t *num_types, int32_t * /*types*/, float * /*max_luminance*/, |
| float * /*max_average_luminance*/, float * /*min_luminance*/) { |
| supported(__func__); |
| *num_types = 0; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetReleaseFences(uint32_t *num_elements, |
| hwc2_layer_t *layers, |
| int32_t *fences) { |
| supported(__func__); |
| uint32_t num_layers = 0; |
| |
| for (std::pair<const hwc2_layer_t, DrmHwcTwo::HwcLayer> &l : layers_) { |
| ++num_layers; |
| if (layers == nullptr || fences == nullptr) |
| continue; |
| |
| if (num_layers > *num_elements) { |
| ALOGW("Overflow num_elements %d/%d", num_layers, *num_elements); |
| return HWC2::Error::None; |
| } |
| |
| layers[num_layers - 1] = l.first; |
| fences[num_layers - 1] = l.second.take_release_fence(); |
| } |
| *num_elements = num_layers; |
| return HWC2::Error::None; |
| } |
| |
| void DrmHwcTwo::HwcDisplay::AddFenceToPresentFence(int fd) { |
| if (fd < 0) |
| return; |
| |
| if (present_fence_.get() >= 0) { |
| int old_fence = present_fence_.get(); |
| present_fence_.Set(sync_merge("dc_present", old_fence, fd)); |
| close(fd); |
| } else { |
| present_fence_.Set(fd); |
| } |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::CreateComposition(bool test) { |
| // order the layers by z-order |
| bool use_client_layer = false; |
| uint32_t client_z_order = UINT32_MAX; |
| std::map<uint32_t, DrmHwcTwo::HwcLayer *> z_map; |
| for (std::pair<const hwc2_layer_t, DrmHwcTwo::HwcLayer> &l : layers_) { |
| switch (l.second.validated_type()) { |
| case HWC2::Composition::Device: |
| z_map.emplace(std::make_pair(l.second.z_order(), &l.second)); |
| break; |
| case HWC2::Composition::Client: |
| // Place it at the z_order of the lowest client layer |
| use_client_layer = true; |
| client_z_order = std::min(client_z_order, l.second.z_order()); |
| break; |
| default: |
| continue; |
| } |
| } |
| if (use_client_layer) |
| z_map.emplace(std::make_pair(client_z_order, &client_layer_)); |
| |
| if (z_map.empty()) |
| return HWC2::Error::BadLayer; |
| |
| std::vector<DrmHwcLayer> composition_layers; |
| |
| // now that they're ordered by z, add them to the composition |
| for (std::pair<const uint32_t, DrmHwcTwo::HwcLayer *> &l : z_map) { |
| DrmHwcLayer layer; |
| l.second->PopulateDrmLayer(&layer); |
| int ret = layer.ImportBuffer(drm_); |
| if (ret) { |
| ALOGE("Failed to import layer, ret=%d", ret); |
| return HWC2::Error::NoResources; |
| } |
| composition_layers.emplace_back(std::move(layer)); |
| } |
| |
| auto composition = std::make_unique<DrmDisplayComposition>(crtc_, |
| planner_.get()); |
| |
| // TODO(nobody): Don't always assume geometry changed |
| int ret = composition->SetLayers(composition_layers.data(), |
| composition_layers.size(), true); |
| if (ret) { |
| ALOGE("Failed to set layers in the composition ret=%d", ret); |
| return HWC2::Error::BadLayer; |
| } |
| |
| std::vector<DrmPlane *> primary_planes(primary_planes_); |
| std::vector<DrmPlane *> overlay_planes(overlay_planes_); |
| ret = composition->Plan(&primary_planes, &overlay_planes); |
| if (ret) { |
| ALOGE("Failed to plan the composition ret=%d", ret); |
| return HWC2::Error::BadConfig; |
| } |
| |
| // Disable the planes we're not using |
| for (auto i = primary_planes.begin(); i != primary_planes.end();) { |
| composition->AddPlaneDisable(*i); |
| i = primary_planes.erase(i); |
| } |
| for (auto i = overlay_planes.begin(); i != overlay_planes.end();) { |
| composition->AddPlaneDisable(*i); |
| i = overlay_planes.erase(i); |
| } |
| |
| if (test) { |
| ret = compositor_.TestComposition(composition.get()); |
| } else { |
| ret = compositor_.ApplyComposition(std::move(composition)); |
| AddFenceToPresentFence(compositor_.TakeOutFence()); |
| } |
| if (ret) { |
| if (!test) |
| ALOGE("Failed to apply the frame composition ret=%d", ret); |
| return HWC2::Error::BadParameter; |
| } |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::PresentDisplay(int32_t *present_fence) { |
| supported(__func__); |
| HWC2::Error ret; |
| |
| ++total_stats_.total_frames_; |
| |
| ret = CreateComposition(false); |
| if (ret != HWC2::Error::None) |
| ++total_stats_.failed_kms_present_; |
| |
| if (ret == HWC2::Error::BadLayer) { |
| // Can we really have no client or device layers? |
| *present_fence = -1; |
| return HWC2::Error::None; |
| } |
| if (ret != HWC2::Error::None) |
| return ret; |
| |
| *present_fence = present_fence_.Release(); |
| |
| ++frame_no_; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetActiveConfig(hwc2_config_t config) { |
| supported(__func__); |
| auto mode = std::find_if(connector_->modes().begin(), |
| connector_->modes().end(), |
| [config](DrmMode const &m) { |
| return m.id() == config; |
| }); |
| if (mode == connector_->modes().end()) { |
| ALOGE("Could not find active mode for %d", config); |
| return HWC2::Error::BadConfig; |
| } |
| |
| auto composition = std::make_unique<DrmDisplayComposition>(crtc_, |
| planner_.get()); |
| int ret = composition->SetDisplayMode(*mode); |
| if (ret) { |
| return HWC2::Error::BadConfig; |
| } |
| ret = compositor_.ApplyComposition(std::move(composition)); |
| if (ret) { |
| ALOGE("Failed to queue dpms composition on %d", ret); |
| return HWC2::Error::BadConfig; |
| } |
| |
| connector_->set_active_mode(*mode); |
| |
| // Setup the client layer's dimensions |
| hwc_rect_t display_frame = {.left = 0, |
| .top = 0, |
| .right = static_cast<int>(mode->h_display()), |
| .bottom = static_cast<int>(mode->v_display())}; |
| client_layer_.SetLayerDisplayFrame(display_frame); |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetClientTarget(buffer_handle_t target, |
| int32_t acquire_fence, |
| int32_t dataspace, |
| hwc_region_t /*damage*/) { |
| supported(__func__); |
| UniqueFd uf(acquire_fence); |
| |
| client_layer_.set_buffer(target); |
| client_layer_.set_acquire_fence(uf.get()); |
| client_layer_.SetLayerDataspace(dataspace); |
| |
| /* TODO: Do not update source_crop every call. |
| * It makes sense to do it once after every hotplug event. */ |
| hwc_drm_bo bo{}; |
| BufferInfoGetter::GetInstance()->ConvertBoInfo(target, &bo); |
| |
| hwc_frect_t source_crop = {.left = 0.0F, |
| .top = 0.0F, |
| .right = bo.width + 0.0F, |
| .bottom = bo.height + 0.0F}; |
| client_layer_.SetLayerSourceCrop(source_crop); |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetColorMode(int32_t mode) { |
| supported(__func__); |
| |
| if (mode < HAL_COLOR_MODE_NATIVE || mode > HAL_COLOR_MODE_BT2100_HLG) |
| return HWC2::Error::BadParameter; |
| |
| if (mode != HAL_COLOR_MODE_NATIVE) |
| return HWC2::Error::Unsupported; |
| |
| color_mode_ = mode; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetColorTransform(const float *matrix, |
| int32_t hint) { |
| supported(__func__); |
| if (hint < HAL_COLOR_TRANSFORM_IDENTITY || |
| hint > HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA) |
| return HWC2::Error::BadParameter; |
| |
| if (!matrix && hint == HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX) |
| return HWC2::Error::BadParameter; |
| |
| color_transform_hint_ = static_cast<android_color_transform_t>(hint); |
| if (color_transform_hint_ == HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX) |
| std::copy(matrix, matrix + MATRIX_SIZE, color_transform_matrix_.begin()); |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetOutputBuffer(buffer_handle_t buffer, |
| int32_t release_fence) { |
| supported(__func__); |
| // TODO(nobody): Need virtual display support |
| return unsupported(__func__, buffer, release_fence); |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetPowerMode(int32_t mode_in) { |
| supported(__func__); |
| uint64_t dpms_value = 0; |
| auto mode = static_cast<HWC2::PowerMode>(mode_in); |
| switch (mode) { |
| case HWC2::PowerMode::Off: |
| dpms_value = DRM_MODE_DPMS_OFF; |
| break; |
| case HWC2::PowerMode::On: |
| dpms_value = DRM_MODE_DPMS_ON; |
| break; |
| case HWC2::PowerMode::Doze: |
| case HWC2::PowerMode::DozeSuspend: |
| return HWC2::Error::Unsupported; |
| default: |
| ALOGI("Power mode %d is unsupported\n", mode); |
| return HWC2::Error::BadParameter; |
| }; |
| |
| auto composition = std::make_unique<DrmDisplayComposition>(crtc_, |
| planner_.get()); |
| composition->SetDpmsMode(dpms_value); |
| int ret = compositor_.ApplyComposition(std::move(composition)); |
| if (ret) { |
| ALOGE("Failed to apply the dpms composition ret=%d", ret); |
| return HWC2::Error::BadParameter; |
| } |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetVsyncEnabled(int32_t enabled) { |
| supported(__func__); |
| vsync_worker_.VSyncControl(HWC2_VSYNC_ENABLE == enabled); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::ValidateDisplay(uint32_t *num_types, |
| uint32_t *num_requests) { |
| supported(__func__); |
| |
| return backend_->ValidateDisplay(this, num_types, num_requests); |
| } |
| |
| std::vector<DrmHwcTwo::HwcLayer *> |
| DrmHwcTwo::HwcDisplay::GetOrderLayersByZPos() { |
| std::vector<DrmHwcTwo::HwcLayer *> ordered_layers; |
| ordered_layers.reserve(layers_.size()); |
| |
| for (auto &[handle, layer] : layers_) { |
| ordered_layers.emplace_back(&layer); |
| } |
| |
| std::sort(std::begin(ordered_layers), std::end(ordered_layers), |
| [](const DrmHwcTwo::HwcLayer *lhs, const DrmHwcTwo::HwcLayer *rhs) { |
| return lhs->z_order() < rhs->z_order(); |
| }); |
| |
| return ordered_layers; |
| } |
| |
| #if PLATFORM_SDK_VERSION > 29 |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayConnectionType(uint32_t *outType) { |
| if (connector_->internal()) |
| *outType = static_cast<uint32_t>(HWC2::DisplayConnectionType::Internal); |
| else if (connector_->external()) |
| *outType = static_cast<uint32_t>(HWC2::DisplayConnectionType::External); |
| else |
| return HWC2::Error::BadConfig; |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayVsyncPeriod( |
| hwc2_vsync_period_t *outVsyncPeriod /* ns */) { |
| supported(__func__); |
| DrmMode const &mode = connector_->active_mode(); |
| if (mode.id() == 0) |
| return HWC2::Error::BadConfig; |
| |
| *outVsyncPeriod = 1E9 / mode.v_refresh(); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetActiveConfigWithConstraints( |
| hwc2_config_t /*config*/, |
| hwc_vsync_period_change_constraints_t *vsyncPeriodChangeConstraints, |
| hwc_vsync_period_change_timeline_t *outTimeline) { |
| supported(__func__); |
| |
| if (vsyncPeriodChangeConstraints == nullptr || outTimeline == nullptr) { |
| return HWC2::Error::BadParameter; |
| } |
| |
| return HWC2::Error::BadConfig; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetAutoLowLatencyMode(bool /*on*/) { |
| return HWC2::Error::Unsupported; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetSupportedContentTypes( |
| uint32_t *outNumSupportedContentTypes, |
| const uint32_t *outSupportedContentTypes) { |
| if (outSupportedContentTypes == nullptr) |
| *outNumSupportedContentTypes = 0; |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetContentType(int32_t contentType) { |
| supported(__func__); |
| |
| if (contentType != HWC2_CONTENT_TYPE_NONE) |
| return HWC2::Error::Unsupported; |
| |
| /* TODO: Map to the DRM Connector property: |
| * https://elixir.bootlin.com/linux/v5.4-rc5/source/drivers/gpu/drm/drm_connector.c#L809 |
| */ |
| |
| return HWC2::Error::None; |
| } |
| #endif |
| |
| #if PLATFORM_SDK_VERSION > 28 |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayIdentificationData( |
| uint8_t *outPort, uint32_t *outDataSize, uint8_t *outData) { |
| supported(__func__); |
| |
| drmModePropertyBlobPtr blob = nullptr; |
| |
| if (connector_->GetEdidBlob(blob)) { |
| ALOGE("Failed to get edid property value."); |
| return HWC2::Error::Unsupported; |
| } |
| |
| if (outData) { |
| *outDataSize = std::min(*outDataSize, blob->length); |
| memcpy(outData, blob->data, *outDataSize); |
| } else { |
| *outDataSize = blob->length; |
| } |
| *outPort = connector_->id(); |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayCapabilities( |
| uint32_t *outNumCapabilities, uint32_t *outCapabilities) { |
| unsupported(__func__, outCapabilities); |
| |
| if (outNumCapabilities == nullptr) { |
| return HWC2::Error::BadParameter; |
| } |
| |
| *outNumCapabilities = 0; |
| |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayBrightnessSupport( |
| bool *supported) { |
| *supported = false; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetDisplayBrightness( |
| float /* brightness */) { |
| return HWC2::Error::Unsupported; |
| } |
| |
| #endif /* PLATFORM_SDK_VERSION > 28 */ |
| |
| #if PLATFORM_SDK_VERSION > 27 |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::GetRenderIntents( |
| int32_t mode, uint32_t *outNumIntents, |
| int32_t * /*android_render_intent_v1_1_t*/ outIntents) { |
| if (mode != HAL_COLOR_MODE_NATIVE) { |
| return HWC2::Error::BadParameter; |
| } |
| |
| if (outIntents == nullptr) { |
| *outNumIntents = 1; |
| return HWC2::Error::None; |
| } |
| *outNumIntents = 1; |
| outIntents[0] = HAL_RENDER_INTENT_COLORIMETRIC; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcDisplay::SetColorModeWithIntent(int32_t mode, |
| int32_t intent) { |
| if (intent < HAL_RENDER_INTENT_COLORIMETRIC || |
| intent > HAL_RENDER_INTENT_TONE_MAP_ENHANCE) |
| return HWC2::Error::BadParameter; |
| |
| if (mode < HAL_COLOR_MODE_NATIVE || mode > HAL_COLOR_MODE_BT2100_HLG) |
| return HWC2::Error::BadParameter; |
| |
| if (mode != HAL_COLOR_MODE_NATIVE) |
| return HWC2::Error::Unsupported; |
| |
| if (intent != HAL_RENDER_INTENT_COLORIMETRIC) |
| return HWC2::Error::Unsupported; |
| |
| color_mode_ = mode; |
| return HWC2::Error::None; |
| } |
| |
| #endif /* PLATFORM_SDK_VERSION > 27 */ |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetCursorPosition(int32_t x, int32_t y) { |
| supported(__func__); |
| cursor_x_ = x; |
| cursor_y_ = y; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerBlendMode(int32_t mode) { |
| supported(__func__); |
| blending_ = static_cast<HWC2::BlendMode>(mode); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerBuffer(buffer_handle_t buffer, |
| int32_t acquire_fence) { |
| supported(__func__); |
| UniqueFd uf(acquire_fence); |
| |
| set_buffer(buffer); |
| set_acquire_fence(uf.get()); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerColor(hwc_color_t color) { |
| // TODO(nobody): Put to client composition here? |
| supported(__func__); |
| layer_color_ = color; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerCompositionType(int32_t type) { |
| sf_type_ = static_cast<HWC2::Composition>(type); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerDataspace(int32_t dataspace) { |
| supported(__func__); |
| dataspace_ = static_cast<android_dataspace_t>(dataspace); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerDisplayFrame(hwc_rect_t frame) { |
| supported(__func__); |
| display_frame_ = frame; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerPlaneAlpha(float alpha) { |
| supported(__func__); |
| alpha_ = alpha; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerSidebandStream( |
| const native_handle_t *stream) { |
| supported(__func__); |
| // TODO(nobody): We don't support sideband |
| return unsupported(__func__, stream); |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerSourceCrop(hwc_frect_t crop) { |
| supported(__func__); |
| source_crop_ = crop; |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerSurfaceDamage(hwc_region_t damage) { |
| supported(__func__); |
| // TODO(nobody): We don't use surface damage, marking as unsupported |
| unsupported(__func__, damage); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerTransform(int32_t transform) { |
| supported(__func__); |
| transform_ = static_cast<HWC2::Transform>(transform); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerVisibleRegion(hwc_region_t visible) { |
| supported(__func__); |
| // TODO(nobody): We don't use this information, marking as unsupported |
| unsupported(__func__, visible); |
| return HWC2::Error::None; |
| } |
| |
| HWC2::Error DrmHwcTwo::HwcLayer::SetLayerZOrder(uint32_t order) { |
| supported(__func__); |
| z_order_ = order; |
| return HWC2::Error::None; |
| } |
| |
| void DrmHwcTwo::HwcLayer::PopulateDrmLayer(DrmHwcLayer *layer) { |
| supported(__func__); |
| switch (blending_) { |
| case HWC2::BlendMode::None: |
| layer->blending = DrmHwcBlending::kNone; |
| break; |
| case HWC2::BlendMode::Premultiplied: |
| layer->blending = DrmHwcBlending::kPreMult; |
| break; |
| case HWC2::BlendMode::Coverage: |
| layer->blending = DrmHwcBlending::kCoverage; |
| break; |
| default: |
| ALOGE("Unknown blending mode b=%d", blending_); |
| layer->blending = DrmHwcBlending::kNone; |
| break; |
| } |
| |
| OutputFd release_fence = release_fence_output(); |
| |
| layer->sf_handle = buffer_; |
| layer->acquire_fence = acquire_fence_.Release(); |
| layer->release_fence = std::move(release_fence); |
| layer->display_frame = display_frame_; |
| layer->alpha = lround(65535.0F * alpha_); |
| layer->source_crop = source_crop_; |
| layer->SetTransform(static_cast<int32_t>(transform_)); |
| layer->dataspace = dataspace_; |
| } |
| |
| void DrmHwcTwo::HandleDisplayHotplug(hwc2_display_t displayid, int state) { |
| const std::lock_guard<std::mutex> lock(hotplug_callback_lock); |
| |
| if (hotplug_callback_hook_ && hotplug_callback_data_) |
| hotplug_callback_hook_(hotplug_callback_data_, displayid, |
| state == DRM_MODE_CONNECTED |
| ? HWC2_CONNECTION_CONNECTED |
| : HWC2_CONNECTION_DISCONNECTED); |
| } |
| |
| void DrmHwcTwo::HandleInitialHotplugState(DrmDevice *drmDevice) { |
| for (const auto &conn : drmDevice->connectors()) { |
| if (conn->state() != DRM_MODE_CONNECTED) |
| continue; |
| HandleDisplayHotplug(conn->display(), conn->state()); |
| } |
| } |
| |
| void DrmHwcTwo::DrmHotplugHandler::HandleEvent(uint64_t timestamp_us) { |
| for (const auto &conn : drm_->connectors()) { |
| drmModeConnection old_state = conn->state(); |
| drmModeConnection cur_state = conn->UpdateModes() |
| ? DRM_MODE_UNKNOWNCONNECTION |
| : conn->state(); |
| |
| if (cur_state == old_state) |
| continue; |
| |
| ALOGI("%s event @%" PRIu64 " for connector %u on display %d", |
| cur_state == DRM_MODE_CONNECTED ? "Plug" : "Unplug", timestamp_us, |
| conn->id(), conn->display()); |
| |
| int display_id = conn->display(); |
| if (cur_state == DRM_MODE_CONNECTED) { |
| auto &display = hwc2_->displays_.at(display_id); |
| display.ChosePreferredConfig(); |
| } else { |
| auto &display = hwc2_->displays_.at(display_id); |
| display.ClearDisplay(); |
| } |
| |
| hwc2_->HandleDisplayHotplug(display_id, cur_state); |
| } |
| } |
| |
| // static |
| int DrmHwcTwo::HookDevClose(hw_device_t * /*dev*/) { |
| unsupported(__func__); |
| return 0; |
| } |
| |
| // static |
| void DrmHwcTwo::HookDevGetCapabilities(hwc2_device_t * /*dev*/, |
| uint32_t *out_count, |
| int32_t * /*out_capabilities*/) { |
| supported(__func__); |
| *out_count = 0; |
| } |
| |
| // static |
| hwc2_function_pointer_t DrmHwcTwo::HookDevGetFunction( |
| struct hwc2_device * /*dev*/, int32_t descriptor) { |
| supported(__func__); |
| auto func = static_cast<HWC2::FunctionDescriptor>(descriptor); |
| switch (func) { |
| // Device functions |
| case HWC2::FunctionDescriptor::CreateVirtualDisplay: |
| return ToHook<HWC2_PFN_CREATE_VIRTUAL_DISPLAY>( |
| DeviceHook<int32_t, decltype(&DrmHwcTwo::CreateVirtualDisplay), |
| &DrmHwcTwo::CreateVirtualDisplay, uint32_t, uint32_t, |
| int32_t *, hwc2_display_t *>); |
| case HWC2::FunctionDescriptor::DestroyVirtualDisplay: |
| return ToHook<HWC2_PFN_DESTROY_VIRTUAL_DISPLAY>( |
| DeviceHook<int32_t, decltype(&DrmHwcTwo::DestroyVirtualDisplay), |
| &DrmHwcTwo::DestroyVirtualDisplay, hwc2_display_t>); |
| case HWC2::FunctionDescriptor::Dump: |
| return ToHook<HWC2_PFN_DUMP>( |
| DeviceHook<void, decltype(&DrmHwcTwo::Dump), &DrmHwcTwo::Dump, |
| uint32_t *, char *>); |
| case HWC2::FunctionDescriptor::GetMaxVirtualDisplayCount: |
| return ToHook<HWC2_PFN_GET_MAX_VIRTUAL_DISPLAY_COUNT>( |
| DeviceHook<uint32_t, decltype(&DrmHwcTwo::GetMaxVirtualDisplayCount), |
| &DrmHwcTwo::GetMaxVirtualDisplayCount>); |
| case HWC2::FunctionDescriptor::RegisterCallback: |
| return ToHook<HWC2_PFN_REGISTER_CALLBACK>( |
| DeviceHook<int32_t, decltype(&DrmHwcTwo::RegisterCallback), |
| &DrmHwcTwo::RegisterCallback, int32_t, |
| hwc2_callback_data_t, hwc2_function_pointer_t>); |
| |
| // Display functions |
| case HWC2::FunctionDescriptor::AcceptDisplayChanges: |
| return ToHook<HWC2_PFN_ACCEPT_DISPLAY_CHANGES>( |
| DisplayHook<decltype(&HwcDisplay::AcceptDisplayChanges), |
| &HwcDisplay::AcceptDisplayChanges>); |
| case HWC2::FunctionDescriptor::CreateLayer: |
| return ToHook<HWC2_PFN_CREATE_LAYER>( |
| DisplayHook<decltype(&HwcDisplay::CreateLayer), |
| &HwcDisplay::CreateLayer, hwc2_layer_t *>); |
| case HWC2::FunctionDescriptor::DestroyLayer: |
| return ToHook<HWC2_PFN_DESTROY_LAYER>( |
| DisplayHook<decltype(&HwcDisplay::DestroyLayer), |
| &HwcDisplay::DestroyLayer, hwc2_layer_t>); |
| case HWC2::FunctionDescriptor::GetActiveConfig: |
| return ToHook<HWC2_PFN_GET_ACTIVE_CONFIG>( |
| DisplayHook<decltype(&HwcDisplay::GetActiveConfig), |
| &HwcDisplay::GetActiveConfig, hwc2_config_t *>); |
| case HWC2::FunctionDescriptor::GetChangedCompositionTypes: |
| return ToHook<HWC2_PFN_GET_CHANGED_COMPOSITION_TYPES>( |
| DisplayHook<decltype(&HwcDisplay::GetChangedCompositionTypes), |
| &HwcDisplay::GetChangedCompositionTypes, uint32_t *, |
| hwc2_layer_t *, int32_t *>); |
| case HWC2::FunctionDescriptor::GetClientTargetSupport: |
| return ToHook<HWC2_PFN_GET_CLIENT_TARGET_SUPPORT>( |
| DisplayHook<decltype(&HwcDisplay::GetClientTargetSupport), |
| &HwcDisplay::GetClientTargetSupport, uint32_t, uint32_t, |
| int32_t, int32_t>); |
| case HWC2::FunctionDescriptor::GetColorModes: |
| return ToHook<HWC2_PFN_GET_COLOR_MODES>( |
| DisplayHook<decltype(&HwcDisplay::GetColorModes), |
| &HwcDisplay::GetColorModes, uint32_t *, int32_t *>); |
| case HWC2::FunctionDescriptor::GetDisplayAttribute: |
| return ToHook<HWC2_PFN_GET_DISPLAY_ATTRIBUTE>( |
| DisplayHook<decltype(&HwcDisplay::GetDisplayAttribute), |
| &HwcDisplay::GetDisplayAttribute, hwc2_config_t, int32_t, |
| int32_t *>); |
| case HWC2::FunctionDescriptor::GetDisplayConfigs: |
| return ToHook<HWC2_PFN_GET_DISPLAY_CONFIGS>( |
| DisplayHook<decltype(&HwcDisplay::GetDisplayConfigs), |
| &HwcDisplay::GetDisplayConfigs, uint32_t *, |
| hwc2_config_t *>); |
| case HWC2::FunctionDescriptor::GetDisplayName: |
| return ToHook<HWC2_PFN_GET_DISPLAY_NAME>( |
| DisplayHook<decltype(&HwcDisplay::GetDisplayName), |
| &HwcDisplay::GetDisplayName, uint32_t *, char *>); |
| case HWC2::FunctionDescriptor::GetDisplayRequests: |
| return ToHook<HWC2_PFN_GET_DISPLAY_REQUESTS>( |
| DisplayHook<decltype(&HwcDisplay::GetDisplayRequests), |
| &HwcDisplay::GetDisplayRequests, int32_t *, uint32_t *, |
| hwc2_layer_t *, int32_t *>); |
| case HWC2::FunctionDescriptor::GetDisplayType: |
| return ToHook<HWC2_PFN_GET_DISPLAY_TYPE>( |
| DisplayHook<decltype(&HwcDisplay::GetDisplayType), |
| &HwcDisplay::GetDisplayType, int32_t *>); |
| case HWC2::FunctionDescriptor::GetDozeSupport: |
| return ToHook<HWC2_PFN_GET_DOZE_SUPPORT>( |
| DisplayHook<decltype(&HwcDisplay::GetDozeSupport), |
| &HwcDisplay::GetDozeSupport, int32_t *>); |
| case HWC2::FunctionDescriptor::GetHdrCapabilities: |
| return ToHook<HWC2_PFN_GET_HDR_CAPABILITIES>( |
| DisplayHook<decltype(&HwcDisplay::GetHdrCapabilities), |
| &HwcDisplay::GetHdrCapabilities, uint32_t *, int32_t *, |
| float *, float *, float *>); |
| case HWC2::FunctionDescriptor::GetReleaseFences: |
| return ToHook<HWC2_PFN_GET_RELEASE_FENCES>( |
| DisplayHook<decltype(&HwcDisplay::GetReleaseFences), |
| &HwcDisplay::GetReleaseFences, uint32_t *, hwc2_layer_t *, |
| int32_t *>); |
| case HWC2::FunctionDescriptor::PresentDisplay: |
| return ToHook<HWC2_PFN_PRESENT_DISPLAY>( |
| DisplayHook<decltype(&HwcDisplay::PresentDisplay), |
| &HwcDisplay::PresentDisplay, int32_t *>); |
| case HWC2::FunctionDescriptor::SetActiveConfig: |
| return ToHook<HWC2_PFN_SET_ACTIVE_CONFIG>( |
| DisplayHook<decltype(&HwcDisplay::SetActiveConfig), |
| &HwcDisplay::SetActiveConfig, hwc2_config_t>); |
| case HWC2::FunctionDescriptor::SetClientTarget: |
| return ToHook<HWC2_PFN_SET_CLIENT_TARGET>( |
| DisplayHook<decltype(&HwcDisplay::SetClientTarget), |
| &HwcDisplay::SetClientTarget, buffer_handle_t, int32_t, |
| int32_t, hwc_region_t>); |
| case HWC2::FunctionDescriptor::SetColorMode: |
| return ToHook<HWC2_PFN_SET_COLOR_MODE>( |
| DisplayHook<decltype(&HwcDisplay::SetColorMode), |
| &HwcDisplay::SetColorMode, int32_t>); |
| case HWC2::FunctionDescriptor::SetColorTransform: |
| return ToHook<HWC2_PFN_SET_COLOR_TRANSFORM>( |
| DisplayHook<decltype(&HwcDisplay::SetColorTransform), |
| &HwcDisplay::SetColorTransform, const float *, int32_t>); |
| case HWC2::FunctionDescriptor::SetOutputBuffer: |
| return ToHook<HWC2_PFN_SET_OUTPUT_BUFFER>( |
| DisplayHook<decltype(&HwcDisplay::SetOutputBuffer), |
| &HwcDisplay::SetOutputBuffer, buffer_handle_t, int32_t>); |
| case HWC2::FunctionDescriptor::SetPowerMode: |
| return ToHook<HWC2_PFN_SET_POWER_MODE>( |
| DisplayHook<decltype(&HwcDisplay::SetPowerMode), |
| &HwcDisplay::SetPowerMode, int32_t>); |
| case HWC2::FunctionDescriptor::SetVsyncEnabled: |
| return ToHook<HWC2_PFN_SET_VSYNC_ENABLED>( |
| DisplayHook<decltype(&HwcDisplay::SetVsyncEnabled), |
| &HwcDisplay::SetVsyncEnabled, int32_t>); |
| case HWC2::FunctionDescriptor::ValidateDisplay: |
| return ToHook<HWC2_PFN_VALIDATE_DISPLAY>( |
| DisplayHook<decltype(&HwcDisplay::ValidateDisplay), |
| &HwcDisplay::ValidateDisplay, uint32_t *, uint32_t *>); |
| #if PLATFORM_SDK_VERSION > 27 |
| case HWC2::FunctionDescriptor::GetRenderIntents: |
| return ToHook<HWC2_PFN_GET_RENDER_INTENTS>( |
| DisplayHook<decltype(&HwcDisplay::GetRenderIntents), |
| &HwcDisplay::GetRenderIntents, int32_t, uint32_t *, |
| int32_t *>); |
| case HWC2::FunctionDescriptor::SetColorModeWithRenderIntent: |
| return ToHook<HWC2_PFN_SET_COLOR_MODE_WITH_RENDER_INTENT>( |
| DisplayHook<decltype(&HwcDisplay::SetColorModeWithIntent), |
| &HwcDisplay::SetColorModeWithIntent, int32_t, int32_t>); |
| #endif |
| #if PLATFORM_SDK_VERSION > 28 |
| case HWC2::FunctionDescriptor::GetDisplayIdentificationData: |
| return ToHook<HWC2_PFN_GET_DISPLAY_IDENTIFICATION_DATA>( |
| DisplayHook<decltype(&HwcDisplay::GetDisplayIdentificationData), |
| &HwcDisplay::GetDisplayIdentificationData, uint8_t *, |
| uint32_t *, uint8_t *>); |
| case HWC2::FunctionDescriptor::GetDisplayCapabilities: |
| return ToHook<HWC2_PFN_GET_DISPLAY_CAPABILITIES>( |
| DisplayHook<decltype(&HwcDisplay::GetDisplayCapabilities), |
| &HwcDisplay::GetDisplayCapabilities, uint32_t *, |
| uint32_t *>); |
| case HWC2::FunctionDescriptor::GetDisplayBrightnessSupport: |
| return ToHook<HWC2_PFN_GET_DISPLAY_BRIGHTNESS_SUPPORT>( |
| DisplayHook<decltype(&HwcDisplay::GetDisplayBrightnessSupport), |
| &HwcDisplay::GetDisplayBrightnessSupport, bool *>); |
| case HWC2::FunctionDescriptor::SetDisplayBrightness: |
| return ToHook<HWC2_PFN_SET_DISPLAY_BRIGHTNESS>( |
| DisplayHook<decltype(&HwcDisplay::SetDisplayBrightness), |
| &HwcDisplay::SetDisplayBrightness, float>); |
| #endif /* PLATFORM_SDK_VERSION > 28 */ |
| #if PLATFORM_SDK_VERSION > 29 |
| case HWC2::FunctionDescriptor::GetDisplayConnectionType: |
| return ToHook<HWC2_PFN_GET_DISPLAY_CONNECTION_TYPE>( |
| DisplayHook<decltype(&HwcDisplay::GetDisplayConnectionType), |
| &HwcDisplay::GetDisplayConnectionType, uint32_t *>); |
| case HWC2::FunctionDescriptor::GetDisplayVsyncPeriod: |
| return ToHook<HWC2_PFN_GET_DISPLAY_VSYNC_PERIOD>( |
| DisplayHook<decltype(&HwcDisplay::GetDisplayVsyncPeriod), |
| &HwcDisplay::GetDisplayVsyncPeriod, |
| hwc2_vsync_period_t *>); |
| case HWC2::FunctionDescriptor::SetActiveConfigWithConstraints: |
| return ToHook<HWC2_PFN_SET_ACTIVE_CONFIG_WITH_CONSTRAINTS>( |
| DisplayHook<decltype(&HwcDisplay::SetActiveConfigWithConstraints), |
| &HwcDisplay::SetActiveConfigWithConstraints, |
| hwc2_config_t, hwc_vsync_period_change_constraints_t *, |
| hwc_vsync_period_change_timeline_t *>); |
| case HWC2::FunctionDescriptor::SetAutoLowLatencyMode: |
| return ToHook<HWC2_PFN_SET_AUTO_LOW_LATENCY_MODE>( |
| DisplayHook<decltype(&HwcDisplay::SetAutoLowLatencyMode), |
| &HwcDisplay::SetAutoLowLatencyMode, bool>); |
| case HWC2::FunctionDescriptor::GetSupportedContentTypes: |
| return ToHook<HWC2_PFN_GET_SUPPORTED_CONTENT_TYPES>( |
| DisplayHook<decltype(&HwcDisplay::GetSupportedContentTypes), |
| &HwcDisplay::GetSupportedContentTypes, uint32_t *, |
| uint32_t *>); |
| case HWC2::FunctionDescriptor::SetContentType: |
| return ToHook<HWC2_PFN_SET_CONTENT_TYPE>( |
| DisplayHook<decltype(&HwcDisplay::SetContentType), |
| &HwcDisplay::SetContentType, int32_t>); |
| #endif |
| // Layer functions |
| case HWC2::FunctionDescriptor::SetCursorPosition: |
| return ToHook<HWC2_PFN_SET_CURSOR_POSITION>( |
| LayerHook<decltype(&HwcLayer::SetCursorPosition), |
| &HwcLayer::SetCursorPosition, int32_t, int32_t>); |
| case HWC2::FunctionDescriptor::SetLayerBlendMode: |
| return ToHook<HWC2_PFN_SET_LAYER_BLEND_MODE>( |
| LayerHook<decltype(&HwcLayer::SetLayerBlendMode), |
| &HwcLayer::SetLayerBlendMode, int32_t>); |
| case HWC2::FunctionDescriptor::SetLayerBuffer: |
| return ToHook<HWC2_PFN_SET_LAYER_BUFFER>( |
| LayerHook<decltype(&HwcLayer::SetLayerBuffer), |
| &HwcLayer::SetLayerBuffer, buffer_handle_t, int32_t>); |
| case HWC2::FunctionDescriptor::SetLayerColor: |
| return ToHook<HWC2_PFN_SET_LAYER_COLOR>( |
| LayerHook<decltype(&HwcLayer::SetLayerColor), |
| &HwcLayer::SetLayerColor, hwc_color_t>); |
| case HWC2::FunctionDescriptor::SetLayerCompositionType: |
| return ToHook<HWC2_PFN_SET_LAYER_COMPOSITION_TYPE>( |
| LayerHook<decltype(&HwcLayer::SetLayerCompositionType), |
| &HwcLayer::SetLayerCompositionType, int32_t>); |
| case HWC2::FunctionDescriptor::SetLayerDataspace: |
| return ToHook<HWC2_PFN_SET_LAYER_DATASPACE>( |
| LayerHook<decltype(&HwcLayer::SetLayerDataspace), |
| &HwcLayer::SetLayerDataspace, int32_t>); |
| case HWC2::FunctionDescriptor::SetLayerDisplayFrame: |
| return ToHook<HWC2_PFN_SET_LAYER_DISPLAY_FRAME>( |
| LayerHook<decltype(&HwcLayer::SetLayerDisplayFrame), |
| &HwcLayer::SetLayerDisplayFrame, hwc_rect_t>); |
| case HWC2::FunctionDescriptor::SetLayerPlaneAlpha: |
| return ToHook<HWC2_PFN_SET_LAYER_PLANE_ALPHA>( |
| LayerHook<decltype(&HwcLayer::SetLayerPlaneAlpha), |
| &HwcLayer::SetLayerPlaneAlpha, float>); |
| case HWC2::FunctionDescriptor::SetLayerSidebandStream: |
| return ToHook<HWC2_PFN_SET_LAYER_SIDEBAND_STREAM>( |
| LayerHook<decltype(&HwcLayer::SetLayerSidebandStream), |
| &HwcLayer::SetLayerSidebandStream, |
| const native_handle_t *>); |
| case HWC2::FunctionDescriptor::SetLayerSourceCrop: |
| return ToHook<HWC2_PFN_SET_LAYER_SOURCE_CROP>( |
| LayerHook<decltype(&HwcLayer::SetLayerSourceCrop), |
| &HwcLayer::SetLayerSourceCrop, hwc_frect_t>); |
| case HWC2::FunctionDescriptor::SetLayerSurfaceDamage: |
| return ToHook<HWC2_PFN_SET_LAYER_SURFACE_DAMAGE>( |
| LayerHook<decltype(&HwcLayer::SetLayerSurfaceDamage), |
| &HwcLayer::SetLayerSurfaceDamage, hwc_region_t>); |
| case HWC2::FunctionDescriptor::SetLayerTransform: |
| return ToHook<HWC2_PFN_SET_LAYER_TRANSFORM>( |
| LayerHook<decltype(&HwcLayer::SetLayerTransform), |
| &HwcLayer::SetLayerTransform, int32_t>); |
| case HWC2::FunctionDescriptor::SetLayerVisibleRegion: |
| return ToHook<HWC2_PFN_SET_LAYER_VISIBLE_REGION>( |
| LayerHook<decltype(&HwcLayer::SetLayerVisibleRegion), |
| &HwcLayer::SetLayerVisibleRegion, hwc_region_t>); |
| case HWC2::FunctionDescriptor::SetLayerZOrder: |
| return ToHook<HWC2_PFN_SET_LAYER_Z_ORDER>( |
| LayerHook<decltype(&HwcLayer::SetLayerZOrder), |
| &HwcLayer::SetLayerZOrder, uint32_t>); |
| case HWC2::FunctionDescriptor::Invalid: |
| default: |
| return nullptr; |
| } |
| } |
| |
| // static |
| int DrmHwcTwo::HookDevOpen(const struct hw_module_t *module, const char *name, |
| struct hw_device_t **dev) { |
| supported(__func__); |
| if (strcmp(name, HWC_HARDWARE_COMPOSER) != 0) { |
| ALOGE("Invalid module name- %s", name); |
| return -EINVAL; |
| } |
| |
| std::unique_ptr<DrmHwcTwo> ctx(new DrmHwcTwo()); |
| if (!ctx) { |
| ALOGE("Failed to allocate DrmHwcTwo"); |
| return -ENOMEM; |
| } |
| |
| HWC2::Error err = ctx->Init(); |
| if (err != HWC2::Error::None) { |
| ALOGE("Failed to initialize DrmHwcTwo err=%d\n", err); |
| return -EINVAL; |
| } |
| |
| ctx->common.module = (hw_module_t *)module; |
| *dev = &ctx->common; |
| ctx.release(); // NOLINT(bugprone-unused-return-value) |
| return 0; |
| } |
| } // namespace android |
| |
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) |
| static struct hw_module_methods_t hwc2_module_methods = { |
| .open = android::DrmHwcTwo::HookDevOpen, |
| }; |
| |
| // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) |
| hw_module_t HAL_MODULE_INFO_SYM = { |
| .tag = HARDWARE_MODULE_TAG, |
| .module_api_version = HARDWARE_MODULE_API_VERSION(2, 0), |
| .id = HWC_HARDWARE_MODULE_ID, |
| .name = "DrmHwcTwo module", |
| .author = "The Android Open Source Project", |
| .methods = &hwc2_module_methods, |
| .dso = nullptr, |
| .reserved = {0}, |
| }; |