/* * 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 "drmdisplaycomposition.h" #include "drmhwcomposer.h" #include "platform.h" #include "vsyncworker.h" #include #include #include #include #include #include namespace android { class DrmVsyncCallback : public VsyncCallback { public: DrmVsyncCallback(hwc2_callback_data_t data, hwc2_function_pointer_t hook) : data_(data), hook_(hook) { } void Callback(int display, int64_t timestamp) { auto hook = reinterpret_cast(hook_); hook(data_, display, timestamp); } private: hwc2_callback_data_t data_; hwc2_function_pointer_t hook_; }; DrmHwcTwo::DrmHwcTwo() { 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); std::shared_ptr importer = resource_manager_.GetImporter(displ); if (!drm || !importer) { ALOGE("Failed to get a valid drmresource and importer"); return HWC2::Error::NoResources; } displays_.emplace(std::piecewise_construct, std::forward_as_tuple(displ), std::forward_as_tuple(&resource_manager_, drm, importer, displ, type)); DrmCrtc *crtc = drm->GetCrtcForDisplay(static_cast(displ)); if (!crtc) { ALOGE("Failed to get crtc for display %d", static_cast(displ)); return HWC2::Error::BadDisplay; } std::vector display_planes; for (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; } } auto &drmDevices = resource_manager_.getDrmDevices(); for (auto &device : drmDevices) { device->RegisterHotplugHandler(new DrmHotplugHandler(this, device.get())); } return ret; } template 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: Implement virtual display return unsupported(__func__, width, height, format, display); } HWC2::Error DrmHwcTwo::DestroyVirtualDisplay(hwc2_display_t display) { // TODO: 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_); return (std::stringstream() << " 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) .str(); } std::string DrmHwcTwo::HwcDisplay::Dump() { auto out = (std::stringstream() << "- 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") .str(); memcpy(&prev_stats_, &total_stats_, sizeof(Stats)); return out; } void DrmHwcTwo::Dump(uint32_t *outSize, char *outBuffer) { supported(__func__); if (outBuffer != nullptr) { auto copiedBytes = mDumpString.copy(outBuffer, *outSize); *outSize = static_cast(copiedBytes); return; } std::stringstream output; output << "-- drm_hwcomposer --\n\n"; for (std::pair &dp : displays_) output << dp.second.Dump(); mDumpString = output.str(); *outSize = static_cast(mDumpString.size()); } uint32_t DrmHwcTwo::GetMaxVirtualDisplayCount() { // TODO: 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__); auto callback = static_cast(descriptor); if (!function) { callbacks_.erase(callback); return HWC2::Error::None; } callbacks_.emplace(callback, HwcCallback(data, function)); switch (callback) { case HWC2::Callback::Hotplug: { auto &drmDevices = resource_manager_.getDrmDevices(); for (auto &device : drmDevices) HandleInitialHotplugState(device.get()); break; } case HWC2::Callback::Refresh: { for (std::pair &d : displays_) d.second.RegisterRefreshCallback(data, function); break; } case HWC2::Callback::Vsync: { for (std::pair &d : displays_) d.second.RegisterVsyncCallback(data, function); break; } default: break; } return HWC2::Error::None; } DrmHwcTwo::HwcDisplay::HwcDisplay(ResourceManager *resource_manager, DrmDevice *drm, std::shared_ptr importer, hwc2_display_t handle, HWC2::DisplayType type) : resource_manager_(resource_manager), drm_(drm), importer_(importer), 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 *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(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("hwc.drm.use_overlay_planes", use_overlay_planes_prop, "1"); bool use_overlay_planes = atoi(use_overlay_planes_prop); 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; } return ChosePreferredConfig(); } HWC2::Error DrmHwcTwo::HwcDisplay::ChosePreferredConfig() { // Fetch the number of modes from the display uint32_t num_configs; HWC2::Error err = GetDisplayConfigs(&num_configs, NULL); if (err != HWC2::Error::None || !num_configs) return err; return SetActiveConfig(connector_->get_preferred_mode_id()); } HWC2::Error DrmHwcTwo::HwcDisplay::RegisterVsyncCallback( hwc2_callback_data_t data, hwc2_function_pointer_t func) { supported(__func__); auto callback = std::make_shared(data, func); vsync_worker_.RegisterCallback(std::move(callback)); return HWC2::Error::None; } void DrmHwcTwo::HwcDisplay::RegisterRefreshCallback( hwc2_callback_data_t data, hwc2_function_pointer_t func) { supported(__func__); auto hook = reinterpret_cast(func); compositor_.SetRefreshCallback([data, hook](int display) { hook(data, static_cast(display)); }); } HWC2::Error DrmHwcTwo::HwcDisplay::AcceptDisplayChanges() { supported(__func__); for (std::pair &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(layer_idx_), HwcLayer()); *layer = static_cast(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 &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(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 min = drm_->min_resolution(); std::pair 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 && dataspace != HAL_DATASPACE_STANDARD_UNSPECIFIED) return HWC2::Error::Unsupported; // TODO: 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(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 * 1000 * 1000 / 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; 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: Remove the following block of code until AOSP handles all modes std::vector 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: Remove this when 3D Attributes are in AOSP if (mode.flags() & DRM_MODE_FLAG_3D_MASK) continue; // TODO: 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: 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(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(static_cast(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: 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(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 &l : layers_) { ++num_layers; if (layers == NULL || fences == NULL) { continue; } else 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); } } bool DrmHwcTwo::HwcDisplay::HardwareSupportsLayerType( HWC2::Composition comp_type) { return comp_type == HWC2::Composition::Device || comp_type == HWC2::Composition::Cursor; } HWC2::Error DrmHwcTwo::HwcDisplay::CreateComposition(bool test) { std::vector layers_map; layers_map.emplace_back(); DrmCompositionDisplayLayersMap &map = layers_map.back(); map.display = static_cast(handle_); map.geometry_changed = true; // TODO: Fix this // order the layers by z-order bool use_client_layer = false; uint32_t client_z_order = UINT32_MAX; std::map z_map; for (std::pair &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; // now that they're ordered by z, add them to the composition for (std::pair &l : z_map) { DrmHwcLayer layer; l.second->PopulateDrmLayer(&layer); int ret = layer.ImportBuffer(importer_.get()); if (ret) { ALOGE("Failed to import layer, ret=%d", ret); return HWC2::Error::NoResources; } map.layers.emplace_back(std::move(layer)); } std::unique_ptr composition = compositor_ .CreateComposition(); composition->Init(drm_, crtc_, importer_.get(), planner_.get(), frame_no_); // TODO: Don't always assume geometry changed int ret = composition->SetLayers(map.layers.data(), map.layers.size(), true); if (ret) { ALOGE("Failed to set layers in the composition ret=%d", ret); return HWC2::Error::BadLayer; } std::vector primary_planes(primary_planes_); std::vector 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; } std::unique_ptr composition = compositor_ .CreateComposition(); composition->Init(drm_, crtc_, importer_.get(), planner_.get(), frame_no_); int ret = composition->SetDisplayMode(*mode); 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(mode->h_display()), .bottom = static_cast(mode->v_display())}; client_layer_.SetLayerDisplayFrame(display_frame); hwc_frect_t source_crop = {.left = 0.0f, .top = 0.0f, .right = mode->h_display() + 0.0f, .bottom = mode->v_display() + 0.0f}; client_layer_.SetLayerSourceCrop(source_crop); 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); return HWC2::Error::None; } HWC2::Error DrmHwcTwo::HwcDisplay::SetColorMode(int32_t mode) { supported(__func__); if (mode != HAL_COLOR_MODE_NATIVE) return HWC2::Error::BadParameter; 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(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: 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(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; }; std::unique_ptr composition = compositor_ .CreateComposition(); composition->Init(drm_, crtc_, importer_.get(), planner_.get(), frame_no_); 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; } uint32_t DrmHwcTwo::HwcDisplay::CalcPixOps( std::map &z_map, size_t first_z, size_t size) { uint32_t pixops = 0; for (std::pair &l : z_map) { if (l.first >= first_z && l.first < first_z + size) { hwc_rect_t df = l.second->display_frame(); pixops += (df.right - df.left) * (df.bottom - df.top); } } return pixops; } void DrmHwcTwo::HwcDisplay::MarkValidated( std::map &z_map, size_t client_first_z, size_t client_size) { for (std::pair &l : z_map) { if (l.first >= client_first_z && l.first < client_first_z + client_size) l.second->set_validated_type(HWC2::Composition::Client); else l.second->set_validated_type(HWC2::Composition::Device); } } HWC2::Error DrmHwcTwo::HwcDisplay::ValidateDisplay(uint32_t *num_types, uint32_t *num_requests) { supported(__func__); *num_types = 0; *num_requests = 0; size_t avail_planes = primary_planes_.size() + overlay_planes_.size(); /* * If more layers then planes, save one plane * for client composited layers */ if (avail_planes < layers_.size()) avail_planes--; std::map z_map, z_map_tmp; uint32_t z_index = 0; // First create a map of layers and z_order values for (std::pair &l : layers_) z_map_tmp.emplace(std::make_pair(l.second.z_order(), &l.second)); // normalise the map so that the lowest z_order layer has key 0 for (std::pair &l : z_map_tmp) z_map.emplace(std::make_pair(z_index++, l.second)); uint32_t total_pixops = CalcPixOps(z_map, 0, z_map.size()), gpu_pixops = 0; int client_start = -1, client_size = 0; if (compositor_.ShouldFlattenOnClient()) { client_start = 0; client_size = z_map.size(); MarkValidated(z_map, client_start, client_size); } else { for (std::pair &l : z_map) { if (!HardwareSupportsLayerType(l.second->sf_type()) || !importer_->CanImportBuffer(l.second->buffer()) || color_transform_hint_ != HAL_COLOR_TRANSFORM_IDENTITY || (l.second->RequireScalingOrPhasing() && resource_manager_->ForcedScalingWithGpu())) { if (client_start < 0) client_start = l.first; client_size = (l.first - client_start) + 1; } } int extra_client = (z_map.size() - client_size) - avail_planes; if (extra_client > 0) { int start = 0, steps; if (client_size != 0) { int prepend = std::min(client_start, extra_client); int append = std::min(int(z_map.size() - (client_start + client_size)), extra_client); start = client_start - prepend; client_size += extra_client; steps = 1 + std::min(std::min(append, prepend), int(z_map.size()) - (start + client_size)); } else { client_size = extra_client; steps = 1 + z_map.size() - extra_client; } gpu_pixops = INT_MAX; for (int i = 0; i < steps; i++) { uint32_t po = CalcPixOps(z_map, start + i, client_size); if (po < gpu_pixops) { gpu_pixops = po; client_start = start + i; } } } MarkValidated(z_map, client_start, client_size); bool testing_needed = !(client_start == 0 && client_size == z_map.size()); if (testing_needed && CreateComposition(true) != HWC2::Error::None) { ++total_stats_.failed_kms_validate_; gpu_pixops = total_pixops; client_size = z_map.size(); MarkValidated(z_map, 0, client_size); } } *num_types = client_size; total_stats_.frames_flattened_ = compositor_.GetFlattenedFramesCount(); total_stats_.gpu_pixops_ += gpu_pixops; total_stats_.total_pixops_ += total_pixops; return *num_types ? HWC2::Error::HasChanges : HWC2::Error::None; } #if PLATFORM_SDK_VERSION > 28 HWC2::Error DrmHwcTwo::HwcDisplay::GetDisplayIdentificationData( uint8_t *outPort, uint32_t *outDataSize, uint8_t *outData) { supported(__func__); drmModePropertyBlobPtr blob; int ret; uint64_t blob_id; std::tie(ret, blob_id) = connector_->edid_property().value(); if (ret) { ALOGE("Failed to get edid property value."); return HWC2::Error::Unsupported; } blob = drmModeGetPropertyBlob(drm_->fd(), blob_id); 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 == NULL) { 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 (mode != HAL_COLOR_MODE_NATIVE) return HWC2::Error::BadParameter; if (intent != HAL_RENDER_INTENT_COLORIMETRIC) return HWC2::Error::BadParameter; 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(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: 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(type); return HWC2::Error::None; } HWC2::Error DrmHwcTwo::HwcLayer::SetLayerDataspace(int32_t dataspace) { supported(__func__); dataspace_ = static_cast(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: 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: 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(transform); return HWC2::Error::None; } HWC2::Error DrmHwcTwo::HwcLayer::SetLayerVisibleRegion(hwc_region_t visible) { supported(__func__); // TODO: 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->SetDisplayFrame(display_frame_); layer->alpha = static_cast(65535.0f * alpha_ + 0.5f); layer->SetSourceCrop(source_crop_); layer->SetTransform(static_cast(transform_)); } void DrmHwcTwo::HandleDisplayHotplug(hwc2_display_t displayid, int state) { auto cb = callbacks_.find(HWC2::Callback::Hotplug); if (cb == callbacks_.end()) return; auto hotplug = reinterpret_cast(cb->second.func); hotplug(cb->second.data, displayid, (state == DRM_MODE_CONNECTED ? HWC2_CONNECTION_CONNECTED : HWC2_CONNECTION_DISCONNECTED)); } void DrmHwcTwo::HandleInitialHotplugState(DrmDevice *drmDevice) { for (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 (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(descriptor); switch (func) { // Device functions case HWC2::FunctionDescriptor::CreateVirtualDisplay: return ToHook( DeviceHook); case HWC2::FunctionDescriptor::DestroyVirtualDisplay: return ToHook( DeviceHook); case HWC2::FunctionDescriptor::Dump: return ToHook( DeviceHook); case HWC2::FunctionDescriptor::GetMaxVirtualDisplayCount: return ToHook( DeviceHook); case HWC2::FunctionDescriptor::RegisterCallback: return ToHook( DeviceHook); // Display functions case HWC2::FunctionDescriptor::AcceptDisplayChanges: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::CreateLayer: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::DestroyLayer: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetActiveConfig: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetChangedCompositionTypes: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetClientTargetSupport: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetColorModes: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetDisplayAttribute: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetDisplayConfigs: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetDisplayName: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetDisplayRequests: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetDisplayType: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetDozeSupport: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetHdrCapabilities: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetReleaseFences: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::PresentDisplay: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::SetActiveConfig: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::SetClientTarget: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::SetColorMode: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::SetColorTransform: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::SetOutputBuffer: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::SetPowerMode: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::SetVsyncEnabled: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::ValidateDisplay: return ToHook( DisplayHook); #if PLATFORM_SDK_VERSION > 27 case HWC2::FunctionDescriptor::GetRenderIntents: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::SetColorModeWithRenderIntent: return ToHook( DisplayHook); #endif #if PLATFORM_SDK_VERSION > 28 case HWC2::FunctionDescriptor::GetDisplayIdentificationData: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetDisplayCapabilities: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::GetDisplayBrightnessSupport: return ToHook( DisplayHook); case HWC2::FunctionDescriptor::SetDisplayBrightness: return ToHook( DisplayHook); #endif /* PLATFORM_SDK_VERSION > 28 */ // Layer functions case HWC2::FunctionDescriptor::SetCursorPosition: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerBlendMode: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerBuffer: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerColor: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerCompositionType: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerDataspace: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerDisplayFrame: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerPlaneAlpha: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerSidebandStream: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerSourceCrop: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerSurfaceDamage: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerTransform: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerVisibleRegion: return ToHook( LayerHook); case HWC2::FunctionDescriptor::SetLayerZOrder: return ToHook( LayerHook); case HWC2::FunctionDescriptor::Invalid: default: return NULL; } } // 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)) { ALOGE("Invalid module name- %s", name); return -EINVAL; } std::unique_ptr 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 = const_cast(module); *dev = &ctx->common; ctx.release(); return 0; } } // namespace android static struct hw_module_methods_t hwc2_module_methods = { .open = android::DrmHwcTwo::HookDevOpen, }; 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 = NULL, .reserved = {0}, };