| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h" |
| |
| #include <drm_fourcc.h> |
| |
| #include <algorithm> |
| #include <set> |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/ozone/platform/drm/gpu/drm_device.h" |
| #include "ui/ozone/platform/drm/gpu/drm_gpu_util.h" |
| #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h" |
| #include "ui/ozone/platform/drm/gpu/scanout_buffer.h" |
| |
| namespace ui { |
| namespace { |
| |
| constexpr float kFixedPointScaleValue = 1 << 16; |
| |
| } // namespace |
| |
| HardwareDisplayPlaneList::HardwareDisplayPlaneList() { |
| atomic_property_set.reset(drmModeAtomicAlloc()); |
| } |
| |
| HardwareDisplayPlaneList::~HardwareDisplayPlaneList() { |
| } |
| |
| HardwareDisplayPlaneList::PageFlipInfo::PageFlipInfo(uint32_t crtc_id, |
| uint32_t framebuffer, |
| CrtcController* crtc) |
| : crtc_id(crtc_id), framebuffer(framebuffer), crtc(crtc) { |
| } |
| |
| HardwareDisplayPlaneList::PageFlipInfo::PageFlipInfo( |
| const PageFlipInfo& other) = default; |
| |
| HardwareDisplayPlaneList::PageFlipInfo::~PageFlipInfo() { |
| } |
| |
| HardwareDisplayPlaneManager::HardwareDisplayPlaneManager() : drm_(nullptr) { |
| } |
| |
| HardwareDisplayPlaneManager::~HardwareDisplayPlaneManager() { |
| } |
| |
| bool HardwareDisplayPlaneManager::Initialize(DrmDevice* drm) { |
| drm_ = drm; |
| |
| // Try to get all of the planes if possible, so we don't have to try to |
| // discover hidden primary planes. |
| #if defined(DRM_CLIENT_CAP_UNIVERSAL_PLANES) |
| has_universal_planes_ = |
| drm_->SetCapability(DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); |
| #endif |
| |
| if (!InitializeCrtcProperties(drm)) |
| return false; |
| |
| if (!InitializePlanes(drm)) |
| return false; |
| |
| std::sort(planes_.begin(), planes_.end(), |
| [](const std::unique_ptr<HardwareDisplayPlane>& l, |
| const std::unique_ptr<HardwareDisplayPlane>& r) { |
| return l->id() < r->id(); |
| }); |
| |
| PopulateSupportedFormats(); |
| return true; |
| } |
| |
| std::unique_ptr<HardwareDisplayPlane> HardwareDisplayPlaneManager::CreatePlane( |
| uint32_t id) { |
| return std::make_unique<HardwareDisplayPlane>(id); |
| } |
| |
| HardwareDisplayPlane* HardwareDisplayPlaneManager::FindNextUnusedPlane( |
| size_t* index, |
| uint32_t crtc_index, |
| const DrmOverlayPlane& overlay) const { |
| for (size_t i = *index; i < planes_.size(); ++i) { |
| auto* plane = planes_[i].get(); |
| if (!plane->in_use() && IsCompatible(plane, overlay, crtc_index)) { |
| *index = i + 1; |
| return plane; |
| } |
| } |
| return nullptr; |
| } |
| |
| int HardwareDisplayPlaneManager::LookupCrtcIndex(uint32_t crtc_id) const { |
| for (size_t i = 0; i < crtc_properties_.size(); ++i) |
| if (crtc_properties_[i].id == crtc_id) |
| return i; |
| return -1; |
| } |
| |
| bool HardwareDisplayPlaneManager::IsCompatible(HardwareDisplayPlane* plane, |
| const DrmOverlayPlane& overlay, |
| uint32_t crtc_index) const { |
| if (plane->type() == HardwareDisplayPlane::kCursor || |
| !plane->CanUseForCrtc(crtc_index)) |
| return false; |
| |
| const uint32_t format = overlay.enable_blend ? |
| overlay.buffer->GetFramebufferPixelFormat() : |
| overlay.buffer->GetOpaqueFramebufferPixelFormat(); |
| if (!plane->IsSupportedFormat(format)) |
| return false; |
| |
| // TODO(kalyank): We should check for z-order and any needed transformation |
| // support. Driver doesn't expose any property to check for z-order, can we |
| // rely on the sorting we do based on plane ids ? |
| |
| return true; |
| } |
| |
| void HardwareDisplayPlaneManager::PopulateSupportedFormats() { |
| std::set<uint32_t> supported_formats; |
| |
| for (const auto& plane : planes_) { |
| const std::vector<uint32_t>& formats = plane->supported_formats(); |
| supported_formats.insert(formats.begin(), formats.end()); |
| } |
| |
| supported_formats_.reserve(supported_formats.size()); |
| supported_formats_.assign(supported_formats.begin(), supported_formats.end()); |
| } |
| |
| void HardwareDisplayPlaneManager::ResetCurrentPlaneList( |
| HardwareDisplayPlaneList* plane_list) const { |
| for (auto* hardware_plane : plane_list->plane_list) { |
| hardware_plane->set_in_use(false); |
| hardware_plane->set_owning_crtc(0); |
| } |
| |
| plane_list->plane_list.clear(); |
| plane_list->legacy_page_flips.clear(); |
| plane_list->atomic_property_set.reset(drmModeAtomicAlloc()); |
| } |
| |
| void HardwareDisplayPlaneManager::BeginFrame( |
| HardwareDisplayPlaneList* plane_list) { |
| for (auto* plane : plane_list->old_plane_list) { |
| plane->set_in_use(false); |
| } |
| } |
| |
| bool HardwareDisplayPlaneManager::AssignOverlayPlanes( |
| HardwareDisplayPlaneList* plane_list, |
| const DrmOverlayPlaneList& overlay_list, |
| uint32_t crtc_id, |
| CrtcController* crtc) { |
| int crtc_index = LookupCrtcIndex(crtc_id); |
| if (crtc_index < 0) { |
| LOG(ERROR) << "Cannot find crtc " << crtc_id; |
| return false; |
| } |
| |
| size_t plane_idx = 0; |
| for (const auto& plane : overlay_list) { |
| HardwareDisplayPlane* hw_plane = |
| FindNextUnusedPlane(&plane_idx, crtc_index, plane); |
| if (!hw_plane) { |
| LOG(ERROR) << "Failed to find a free plane for crtc " << crtc_id; |
| ResetCurrentPlaneList(plane_list); |
| return false; |
| } |
| |
| gfx::Rect fixed_point_rect; |
| if (hw_plane->type() != HardwareDisplayPlane::kDummy) { |
| const gfx::Size& size = plane.buffer->GetSize(); |
| gfx::RectF crop_rect = plane.crop_rect; |
| crop_rect.Scale(size.width(), size.height()); |
| |
| // This returns a number in 16.16 fixed point, required by the DRM overlay |
| // APIs. |
| auto to_fixed_point = |
| [](double v) -> uint32_t { return v * kFixedPointScaleValue; }; |
| fixed_point_rect = gfx::Rect(to_fixed_point(crop_rect.x()), |
| to_fixed_point(crop_rect.y()), |
| to_fixed_point(crop_rect.width()), |
| to_fixed_point(crop_rect.height())); |
| } |
| |
| if (!SetPlaneData(plane_list, hw_plane, plane, crtc_id, fixed_point_rect, |
| crtc)) { |
| ResetCurrentPlaneList(plane_list); |
| return false; |
| } |
| |
| plane_list->plane_list.push_back(hw_plane); |
| hw_plane->set_owning_crtc(crtc_id); |
| hw_plane->set_in_use(true); |
| } |
| return true; |
| } |
| |
| const std::vector<uint32_t>& HardwareDisplayPlaneManager::GetSupportedFormats() |
| const { |
| return supported_formats_; |
| } |
| |
| std::vector<uint64_t> HardwareDisplayPlaneManager::GetFormatModifiers( |
| uint32_t crtc_id, |
| uint32_t format) { |
| int crtc_index = LookupCrtcIndex(crtc_id); |
| |
| for (const auto& plane : planes_) { |
| if (plane->CanUseForCrtc(crtc_index) && |
| plane->type() == HardwareDisplayPlane::kPrimary) { |
| return plane->ModifiersForFormat(format); |
| } |
| } |
| |
| return std::vector<uint64_t>(); |
| } |
| |
| bool HardwareDisplayPlaneManager::SetColorMatrix( |
| uint32_t crtc_id, |
| const std::vector<float>& color_matrix) { |
| if (color_matrix.empty()) { |
| // TODO: Consider allowing an empty matrix to disable the color transform |
| // matrix. |
| LOG(ERROR) << "CTM is empty. Expected a 3x3 matrix."; |
| return false; |
| } |
| |
| const int crtc_index = LookupCrtcIndex(crtc_id); |
| DCHECK_GE(crtc_index, 0); |
| CrtcProperties* crtc_props = &crtc_properties_[crtc_index]; |
| |
| ScopedDrmColorCtmPtr ctm_blob_data = CreateCTMBlob(color_matrix); |
| if (!crtc_props->ctm.id) |
| return SetColorCorrectionOnAllCrtcPlanes(crtc_id, std::move(ctm_blob_data)); |
| |
| ScopedDrmPropertyBlob ctm_prop = |
| drm_->CreatePropertyBlob(ctm_blob_data.get(), sizeof(drm_color_ctm)); |
| crtc_props->ctm.value = ctm_prop->id(); |
| return CommitColorMatrix(*crtc_props); |
| } |
| |
| bool HardwareDisplayPlaneManager::SetGammaCorrection( |
| uint32_t crtc_id, |
| const std::vector<display::GammaRampRGBEntry>& degamma_lut, |
| const std::vector<display::GammaRampRGBEntry>& gamma_lut) { |
| const int crtc_index = LookupCrtcIndex(crtc_id); |
| if (crtc_index < 0) { |
| LOG(ERROR) << "Unknown CRTC ID=" << crtc_id; |
| return false; |
| } |
| |
| CrtcProperties* crtc_props = &crtc_properties_[crtc_index]; |
| |
| if (!degamma_lut.empty() && |
| (!crtc_props->degamma_lut.id || !crtc_props->degamma_lut_size.id)) |
| return false; |
| |
| if (!gamma_lut.empty() && |
| (!crtc_props->gamma_lut.id || !crtc_props->gamma_lut_size.id)) { |
| // If we can't find the degamma & gamma lut, it means the properties |
| // aren't available. We should then try to use the legacy gamma ramp ioctl. |
| if (degamma_lut.empty()) |
| return drm_->SetGammaRamp(crtc_id, gamma_lut); |
| |
| // We're missing either degamma or gamma lut properties. We shouldn't try to |
| // set just one of them. |
| return false; |
| } |
| |
| ScopedDrmColorLutPtr degamma_blob_data = CreateLutBlob( |
| ResampleLut(degamma_lut, crtc_props->degamma_lut_size.value)); |
| ScopedDrmColorLutPtr gamma_blob_data = |
| CreateLutBlob(ResampleLut(gamma_lut, crtc_props->gamma_lut_size.value)); |
| |
| ScopedDrmPropertyBlob degamma_prop, gamma_prop; |
| if (degamma_blob_data) { |
| degamma_prop = drm_->CreatePropertyBlob( |
| degamma_blob_data.get(), |
| sizeof(drm_color_lut) * crtc_props->degamma_lut_size.value); |
| crtc_props->degamma_lut.value = degamma_prop->id(); |
| } else { |
| crtc_props->degamma_lut.value = 0; |
| } |
| |
| if (gamma_blob_data) { |
| gamma_prop = drm_->CreatePropertyBlob( |
| gamma_blob_data.get(), |
| sizeof(drm_color_lut) * crtc_props->gamma_lut_size.value); |
| crtc_props->gamma_lut.value = gamma_prop->id(); |
| } else { |
| crtc_props->gamma_lut.value = 0; |
| } |
| |
| return CommitGammaCorrection(*crtc_props); |
| } |
| |
| bool HardwareDisplayPlaneManager::InitializeCrtcProperties(DrmDevice* drm) { |
| ScopedDrmResourcesPtr resources(drm->GetResources()); |
| if (!resources) { |
| PLOG(ERROR) << "Failed to get resources."; |
| return false; |
| } |
| |
| unsigned int num_crtcs_with_out_fence_ptr = 0; |
| |
| for (int i = 0; i < resources->count_crtcs; ++i) { |
| CrtcProperties p{}; |
| p.id = resources->crtcs[i]; |
| |
| ScopedDrmObjectPropertyPtr props( |
| drm->GetObjectProperties(resources->crtcs[i], DRM_MODE_OBJECT_CRTC)); |
| if (!props) { |
| PLOG(ERROR) << "Failed to get CRTC properties for crtc_id=" << p.id; |
| continue; |
| } |
| |
| // These properties are optional. If they don't exist we can tell by the |
| // invalid ID. |
| GetDrmPropertyForName(drm, props.get(), "CTM", &p.ctm); |
| GetDrmPropertyForName(drm, props.get(), "GAMMA_LUT", &p.gamma_lut); |
| GetDrmPropertyForName(drm, props.get(), "GAMMA_LUT_SIZE", |
| &p.gamma_lut_size); |
| GetDrmPropertyForName(drm, props.get(), "DEGAMMA_LUT", &p.degamma_lut); |
| GetDrmPropertyForName(drm, props.get(), "DEGAMMA_LUT_SIZE", |
| &p.degamma_lut_size); |
| GetDrmPropertyForName(drm, props.get(), "OUT_FENCE_PTR", &p.out_fence_ptr); |
| |
| num_crtcs_with_out_fence_ptr += (p.out_fence_ptr.id != 0); |
| |
| crtc_properties_.push_back(p); |
| } |
| |
| // Check that either all or none of the crtcs support the OUT_FENCE_PTR |
| // property. Otherwise we will get an incomplete, and thus not useful, |
| // out-fence set when we perform a commit involving the problematic |
| // crtcs. |
| if (num_crtcs_with_out_fence_ptr != 0 && |
| num_crtcs_with_out_fence_ptr != crtc_properties_.size()) { |
| LOG(ERROR) << "Only some of the crtcs support the OUT_FENCE_PTR property"; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace ui |