| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <drm_fourcc.h> |
| #include <stdint.h> |
| #include <unistd.h> |
| #include <xf86drmMode.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "base/notreached.h" |
| #include "base/posix/eintr_wrapper.h" |
| #include "base/test/task_environment.h" |
| #include "base/time/time.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/display/types/display_color_management.h" |
| #include "ui/gfx/gpu_fence.h" |
| #include "ui/gfx/gpu_fence_handle.h" |
| #include "ui/gfx/linux/gbm_buffer.h" |
| #include "ui/gfx/linux/test/mock_gbm_device.h" |
| #include "ui/gfx/overlay_transform.h" |
| #include "ui/ozone/platform/drm/common/drm_util.h" |
| #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h" |
| #include "ui/ozone/platform/drm/gpu/drm_gpu_util.h" |
| #include "ui/ozone/platform/drm/gpu/hardware_display_plane_atomic.h" |
| #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h" |
| #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h" |
| #include "ui/ozone/platform/drm/gpu/mock_drm_device.h" |
| #include "ui/ozone/platform/drm/gpu/page_flip_request.h" |
| |
| namespace ui { |
| |
| namespace { |
| |
| // TODO(https://crbug.com/1505062): These tests should not use a single-point |
| // curve as the non-empty value (it is arguably not a valid input). |
| const display::GammaCurve kNonemptyGammaCurve({{0, 0, 0}}); |
| const display::GammaCurve kEmptyGammaCurve; |
| |
| const gfx::Size kDefaultBufferSize(2, 2); |
| // Create a basic mode for a 6x4 screen. |
| drmModeModeInfo kDefaultMode = {.hdisplay = 6, .vdisplay = 4}; |
| |
| } // namespace |
| |
| class HardwareDisplayPlaneManagerTest |
| : public testing::Test, |
| public testing::WithParamInterface<bool> { |
| public: |
| HardwareDisplayPlaneManagerTest() = default; |
| |
| HardwareDisplayPlaneManagerTest(const HardwareDisplayPlaneManagerTest&) = |
| delete; |
| HardwareDisplayPlaneManagerTest& operator=( |
| const HardwareDisplayPlaneManagerTest&) = delete; |
| |
| uint64_t GetObjectPropertyValue(uint32_t object_id, |
| uint32_t object_type, |
| const std::string& property_name); |
| uint64_t GetCrtcPropertyValue(uint32_t crtc, |
| const std::string& property_name); |
| uint64_t GetPlanePropertyValue(uint32_t plane, |
| const std::string& property_name); |
| |
| void PerformPageFlip(size_t crtc_idx, HardwareDisplayPlaneList* state); |
| void PerformPageFlip(size_t crtc_idx, |
| HardwareDisplayPlaneList* state, |
| DrmOverlayPlaneList& assigns); |
| void PerformFailingPageFlip(size_t crtc_idx, |
| HardwareDisplayPlaneList* state, |
| DrmOverlayPlaneList& assigns); |
| |
| void SetUp() override; |
| |
| scoped_refptr<DrmFramebuffer> CreateBuffer(const gfx::Size& size) { |
| return CreateBufferWithFormat(size, DRM_FORMAT_XRGB8888); |
| } |
| |
| scoped_refptr<DrmFramebuffer> CreateBufferWithFormat(const gfx::Size& size, |
| uint32_t format) { |
| std::unique_ptr<GbmBuffer> buffer = |
| fake_drm_->gbm_device()->CreateBuffer(format, size, GBM_BO_USE_SCANOUT); |
| return DrmFramebuffer::AddFramebuffer(fake_drm_, buffer.get(), size); |
| } |
| |
| protected: |
| HardwareDisplayPlaneList state_; |
| scoped_refptr<DrmFramebuffer> fake_buffer_; |
| scoped_refptr<MockDrmDevice> fake_drm_; |
| |
| bool use_atomic_ = false; |
| }; |
| |
| void HardwareDisplayPlaneManagerTest::SetUp() { |
| use_atomic_ = GetParam(); |
| |
| auto gbm_device = std::make_unique<MockGbmDevice>(); |
| fake_drm_ = new MockDrmDevice(std::move(gbm_device)); |
| fake_drm_->SetPropertyBlob(MockDrmDevice::AllocateInFormatsBlob( |
| kInFormatsBlobIdBase, {DRM_FORMAT_XRGB8888}, {})); |
| |
| fake_buffer_ = CreateBuffer(kDefaultBufferSize); |
| } |
| |
| void HardwareDisplayPlaneManagerTest::PerformPageFlip( |
| size_t crtc_idx, |
| HardwareDisplayPlaneList* state) { |
| DrmOverlayPlaneList assigns; |
| scoped_refptr<DrmFramebuffer> xrgb_buffer = CreateBuffer(kDefaultBufferSize); |
| assigns.emplace_back(xrgb_buffer, nullptr); |
| PerformPageFlip(crtc_idx, state, assigns); |
| } |
| |
| void HardwareDisplayPlaneManagerTest::PerformPageFlip( |
| size_t crtc_idx, |
| HardwareDisplayPlaneList* state, |
| DrmOverlayPlaneList& assigns) { |
| fake_drm_->plane_manager()->BeginFrame(state); |
| ASSERT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| state, assigns, fake_drm_->crtc_property(crtc_idx).id)); |
| scoped_refptr<PageFlipRequest> page_flip_request = |
| base::MakeRefCounted<PageFlipRequest>(base::TimeDelta()); |
| |
| fake_drm_->set_commit_expectation(true); |
| |
| ASSERT_TRUE( |
| fake_drm_->plane_manager()->Commit(state, page_flip_request, nullptr)); |
| } |
| |
| void HardwareDisplayPlaneManagerTest::PerformFailingPageFlip( |
| size_t crtc_idx, |
| HardwareDisplayPlaneList* state, |
| DrmOverlayPlaneList& assigns) { |
| fake_drm_->plane_manager()->BeginFrame(state); |
| ASSERT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| state, assigns, fake_drm_->crtc_property(crtc_idx).id)); |
| scoped_refptr<PageFlipRequest> page_flip_request = |
| base::MakeRefCounted<PageFlipRequest>(base::TimeDelta()); |
| |
| fake_drm_->set_commit_expectation(false); |
| |
| ASSERT_FALSE( |
| fake_drm_->plane_manager()->Commit(state, page_flip_request, nullptr)); |
| } |
| |
| uint64_t HardwareDisplayPlaneManagerTest::GetObjectPropertyValue( |
| uint32_t object_id, |
| uint32_t object_type, |
| const std::string& property_name) { |
| DrmDevice::Property p{}; |
| ScopedDrmObjectPropertyPtr properties( |
| fake_drm_->GetObjectProperties(object_id, object_type)); |
| EXPECT_TRUE(GetDrmPropertyForName(fake_drm_.get(), properties.get(), |
| property_name, &p)); |
| return p.value; |
| } |
| |
| uint64_t HardwareDisplayPlaneManagerTest::GetCrtcPropertyValue( |
| uint32_t crtc, |
| const std::string& property_name) { |
| return GetObjectPropertyValue(crtc, DRM_MODE_OBJECT_CRTC, property_name); |
| } |
| |
| uint64_t HardwareDisplayPlaneManagerTest::GetPlanePropertyValue( |
| uint32_t plane, |
| const std::string& property_name) { |
| return GetObjectPropertyValue(plane, DRM_MODE_OBJECT_PLANE, property_name); |
| } |
| |
| using HardwareDisplayPlaneManagerLegacyTest = HardwareDisplayPlaneManagerTest; |
| using HardwareDisplayPlaneManagerAtomicTest = HardwareDisplayPlaneManagerTest; |
| |
| // TODO(crbug.com/1431767): Re-enable this test |
| #if defined(LEAK_SANITIZER) |
| #define MAYBE_ResettingConnectorCache DISABLED_ResettingConnectorCache |
| #else |
| #define MAYBE_ResettingConnectorCache ResettingConnectorCache |
| #endif |
| TEST_P(HardwareDisplayPlaneManagerTest, MAYBE_ResettingConnectorCache) { |
| const int connector_and_crtc_count = 3; |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| connector_and_crtc_count, |
| /*planes_per_crtc=*/1); |
| |
| drm_state.connector_properties.clear(); |
| // Create 3 connectors, kConnectorIdBase + 0/1/2 |
| for (size_t i = 0; i < connector_and_crtc_count; ++i) { |
| auto& connector_properties = drm_state.connector_properties.emplace_back(); |
| connector_properties.id = kConnectorIdBase + i; |
| connector_properties.properties.push_back( |
| {.id = kCrtcIdPropId, .value = 0}); |
| } |
| |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/true); |
| |
| HardwareDisplayPlaneList state; |
| |
| { |
| CommitRequest commit_request; |
| fake_drm_->plane_manager()->BeginFrame(&state); |
| // Check all 3 connectors exist |
| for (size_t i = 0; i < connector_and_crtc_count; ++i) { |
| DrmOverlayPlaneList overlays; |
| overlays.emplace_back(fake_buffer_, nullptr); |
| |
| CrtcCommitRequest request = CrtcCommitRequest::EnableCrtcRequest( |
| fake_drm_->crtc_property(i).id, fake_drm_->connector_property(i).id, |
| kDefaultMode, gfx::Point(), &state, std::move(overlays), |
| /*enable_vrr=*/false); |
| commit_request.push_back(std::move(request)); |
| } |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit( |
| std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| } |
| |
| // Replace last connector and update state. |
| drm_state.connector_properties[connector_and_crtc_count - 1].id = |
| kConnectorIdBase + 3; |
| fake_drm_->UpdateStateBesidesPlaneManager(drm_state); |
| fake_drm_->plane_manager()->ResetConnectorsCacheAndGetValidIds( |
| fake_drm_->GetResources()); |
| |
| { |
| CommitRequest commit_request; |
| fake_drm_->plane_manager()->BeginFrame(&state); |
| { |
| DrmOverlayPlaneList overlays; |
| overlays.emplace_back(fake_buffer_, nullptr); |
| commit_request.push_back(CrtcCommitRequest::EnableCrtcRequest( |
| fake_drm_->crtc_property(0).id, kConnectorIdBase, kDefaultMode, |
| gfx::Point(), &state, std::move(overlays), /*enable_vrr=*/false)); |
| } |
| { |
| DrmOverlayPlaneList overlays; |
| overlays.emplace_back(fake_buffer_, nullptr); |
| commit_request.push_back(CrtcCommitRequest::EnableCrtcRequest( |
| fake_drm_->crtc_property(1).id, kConnectorIdBase + 1, kDefaultMode, |
| gfx::Point(), &state, std::move(overlays), /*enable_vrr=*/false)); |
| } |
| { |
| DrmOverlayPlaneList overlays; |
| overlays.emplace_back(fake_buffer_, nullptr); |
| commit_request.push_back(CrtcCommitRequest::EnableCrtcRequest( |
| fake_drm_->crtc_property(2).id, kConnectorIdBase + 3, kDefaultMode, |
| gfx::Point(), &state, std::move(overlays), /*enable_vrr=*/false)); |
| } |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit( |
| std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| } |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerTest, SequenceIncrementOnModesetOnly) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithNoProperties(); |
| // Add some resources so HardwareDisplayPlaneManager can properly initialize |
| // within |fake_drm_|. |
| drm_state.AddCrtcAndConnector(); |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/true); |
| |
| // Modeset Test |
| { |
| int pre_test_sequence_id = fake_drm_->modeset_sequence_id(); |
| ASSERT_TRUE(fake_drm_->plane_manager()->Commit( |
| CommitRequest(), |
| DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| EXPECT_EQ(pre_test_sequence_id, fake_drm_->modeset_sequence_id()); |
| } |
| |
| // Successful Modeset |
| { |
| int pre_modeset_sequence_id = fake_drm_->modeset_sequence_id(); |
| ASSERT_TRUE(fake_drm_->plane_manager()->Commit( |
| CommitRequest(), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| EXPECT_EQ(pre_modeset_sequence_id + 1, fake_drm_->modeset_sequence_id()); |
| } |
| |
| // Failed Modeset |
| { |
| int pre_modeset_sequence_id = fake_drm_->modeset_sequence_id(); |
| fake_drm_->set_set_crtc_expectation(false); |
| ASSERT_FALSE(fake_drm_->plane_manager()->Commit( |
| CommitRequest(), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| fake_drm_->set_set_crtc_expectation(true); |
| EXPECT_EQ(pre_modeset_sequence_id, fake_drm_->modeset_sequence_id()); |
| } |
| |
| // Page Flip |
| { |
| int pre_flip_sequence_id = fake_drm_->modeset_sequence_id(); |
| ASSERT_TRUE(fake_drm_->plane_manager()->Commit(CommitRequest(), |
| DRM_MODE_ATOMIC_NONBLOCK)); |
| EXPECT_EQ(pre_flip_sequence_id, fake_drm_->modeset_sequence_id()); |
| } |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerLegacyTest, Modeset) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/false); |
| |
| fake_drm_->set_set_crtc_expectation(false); |
| |
| HardwareDisplayPlaneList state; |
| DrmOverlayPlane plane(fake_buffer_, nullptr); |
| CommitRequest commit_request; |
| |
| DrmOverlayPlaneList overlays; |
| overlays.push_back(plane.Clone()); |
| commit_request.push_back(CrtcCommitRequest::EnableCrtcRequest( |
| fake_drm_->crtc_property(0).id, fake_drm_->connector_property(0).id, |
| kDefaultMode, gfx::Point(), &state, std::move(overlays), |
| /*enable_vrr=*/false)); |
| EXPECT_FALSE(fake_drm_->plane_manager()->Commit( |
| std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| |
| EXPECT_EQ(plane.buffer->framebuffer_id(), fake_drm_->current_framebuffer()); |
| EXPECT_EQ(1, fake_drm_->get_set_crtc_call_count()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerLegacyTest, DisableModeset) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/false); |
| |
| HardwareDisplayPlaneList state; |
| CommitRequest commit_request; |
| commit_request.push_back(CrtcCommitRequest::DisableCrtcRequest( |
| fake_drm_->crtc_property(0).id, fake_drm_->connector_property(0).id, |
| &state)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit( |
| std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerLegacyTest, SinglePlaneAssignment) { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_EQ(1u, state_.plane_list.size()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerLegacyTest, AddCursor) { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| bool cursor_found = false; |
| for (const auto& plane : fake_drm_->plane_manager()->planes()) { |
| if (plane->type() == DRM_PLANE_TYPE_CURSOR) { |
| cursor_found = true; |
| break; |
| } |
| } |
| EXPECT_TRUE(cursor_found); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerLegacyTest, BadCrtc) { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_FALSE( |
| fake_drm_->plane_manager()->AssignOverlayPlanes(&state_, assigns, 0)); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerLegacyTest, NotEnoughPlanes) { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerLegacyTest, MultipleCrtcs) { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(1).id)); |
| EXPECT_EQ(2u, state_.plane_list.size()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerLegacyTest, MultiplePlanesAndCrtcs) { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/2); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(1).id)); |
| EXPECT_EQ(0u, state_.plane_list.size()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerLegacyTest, CheckFramebufferFormatMatch) { |
| DrmOverlayPlaneList assigns; |
| scoped_refptr<DrmFramebuffer> buffer = |
| CreateBufferWithFormat(kDefaultBufferSize, DRM_FORMAT_NV12); |
| assigns.emplace_back(buffer, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| fake_drm_->plane_manager()->BeginFrame(&state_); |
| // This should return false as plane manager creates planes which support |
| // DRM_FORMAT_XRGB8888 while buffer returns kDummyFormat as its pixelFormat. |
| EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| assigns.clear(); |
| scoped_refptr<DrmFramebuffer> xrgb_buffer = CreateBuffer(kDefaultBufferSize); |
| assigns.emplace_back(xrgb_buffer, nullptr); |
| fake_drm_->plane_manager()->BeginFrame(&state_); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| fake_drm_->plane_manager()->BeginFrame(&state_); |
| EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| } |
| |
| // TODO(crbug.com/1431767): Re-enable this test |
| #if defined(LEAK_SANITIZER) |
| #define MAYBE_Modeset DISABLED_Modeset |
| #else |
| #define MAYBE_Modeset Modeset |
| #endif |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, MAYBE_Modeset) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/true); |
| |
| HardwareDisplayPlaneList state; |
| CommitRequest commit_request; |
| DrmOverlayPlaneList overlays; |
| overlays.emplace_back(fake_buffer_, nullptr); |
| |
| commit_request.push_back(CrtcCommitRequest::EnableCrtcRequest( |
| fake_drm_->crtc_property(0).id, fake_drm_->connector_property(0).id, |
| kDefaultMode, gfx::Point(), &state, std::move(overlays), |
| /*enable_vrr=*/false)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit( |
| std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| |
| EXPECT_EQ(1, fake_drm_->get_commit_count()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, DisableModeset) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/true); |
| |
| HardwareDisplayPlaneList state; |
| CommitRequest commit_request; |
| commit_request.push_back(CrtcCommitRequest::DisableCrtcRequest( |
| fake_drm_->crtc_property(0).id, fake_drm_->connector_property(0).id, |
| &state)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit( |
| std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| |
| EXPECT_EQ(1, fake_drm_->get_commit_count()); |
| } |
| |
| // TODO(crbug.com/1431767): Re-enable this test |
| #if defined(LEAK_SANITIZER) |
| #define MAYBE_CheckPropsAfterModeset DISABLED_CheckPropsAfterModeset |
| #else |
| #define MAYBE_CheckPropsAfterModeset CheckPropsAfterModeset |
| #endif |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, MAYBE_CheckPropsAfterModeset) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/true); |
| |
| HardwareDisplayPlaneList state; |
| CommitRequest commit_request; |
| DrmOverlayPlaneList overlays; |
| overlays.emplace_back(fake_buffer_, nullptr); |
| commit_request.push_back(CrtcCommitRequest::EnableCrtcRequest( |
| fake_drm_->crtc_property(0).id, fake_drm_->connector_property(0).id, |
| kDefaultMode, gfx::Point(), &state, std::move(overlays), |
| /*enable_vrr=*/false)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit( |
| std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| |
| // Test props values after modesetting. |
| DrmDevice::Property connector_prop_crtc_id; |
| ScopedDrmObjectPropertyPtr connector_props = fake_drm_->GetObjectProperties( |
| kConnectorIdBase, DRM_MODE_OBJECT_CONNECTOR); |
| GetDrmPropertyForName(fake_drm_.get(), connector_props.get(), "CRTC_ID", |
| &connector_prop_crtc_id); |
| EXPECT_EQ(kCrtcIdPropId, connector_prop_crtc_id.id); |
| |
| DrmDevice::Property crtc_prop_for_name; |
| ScopedDrmObjectPropertyPtr crtc_props = |
| fake_drm_->GetObjectProperties(kCrtcIdBase, DRM_MODE_OBJECT_CRTC); |
| GetDrmPropertyForName(fake_drm_.get(), crtc_props.get(), "ACTIVE", |
| &crtc_prop_for_name); |
| EXPECT_EQ(kActivePropId, crtc_prop_for_name.id); |
| EXPECT_EQ(1U, crtc_prop_for_name.value); |
| |
| GetDrmPropertyForName(fake_drm_.get(), crtc_props.get(), "MODE_ID", |
| &crtc_prop_for_name); |
| EXPECT_EQ(kModePropId, crtc_prop_for_name.id); |
| } |
| |
| // TODO(crbug.com/1431767): Re-enable this test |
| #if defined(LEAK_SANITIZER) |
| #define MAYBE_CheckPropsAfterDisable DISABLED_CheckPropsAfterDisable |
| #else |
| #define MAYBE_CheckPropsAfterDisable CheckPropsAfterDisable |
| #endif |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, MAYBE_CheckPropsAfterDisable) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/true); |
| |
| HardwareDisplayPlaneList state; |
| { |
| CommitRequest commit_request; |
| DrmOverlayPlaneList overlays; |
| overlays.emplace_back(fake_buffer_, nullptr); |
| commit_request.push_back(CrtcCommitRequest::EnableCrtcRequest( |
| fake_drm_->crtc_property(0).id, fake_drm_->connector_property(0).id, |
| kDefaultMode, gfx::Point(), &state, std::move(overlays), |
| /*enable_vrr=*/false)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit( |
| std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| } |
| |
| // Test props values after disabling. |
| { |
| CommitRequest commit_request; |
| commit_request.push_back(CrtcCommitRequest::DisableCrtcRequest( |
| fake_drm_->crtc_property(0).id, fake_drm_->connector_property(0).id, |
| &state)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit( |
| std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| } |
| |
| DrmDevice::Property crtc_prop_for_name; |
| ScopedDrmObjectPropertyPtr crtc_props = |
| fake_drm_->GetObjectProperties(kCrtcIdBase, DRM_MODE_OBJECT_CRTC); |
| GetDrmPropertyForName(fake_drm_.get(), crtc_props.get(), "ACTIVE", |
| &crtc_prop_for_name); |
| EXPECT_EQ(kActivePropId, crtc_prop_for_name.id); |
| EXPECT_EQ(0U, crtc_prop_for_name.value); |
| } |
| |
| // TODO(crbug.com/1431767): Re-enable this test |
| #if defined(LEAK_SANITIZER) |
| #define MAYBE_CheckVrrAfterModeset DISABLED_CheckVrrAfterModeset |
| #else |
| #define MAYBE_CheckVrrAfterModeset CheckVrrAfterModeset |
| #endif |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, MAYBE_CheckVrrAfterModeset) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/2); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kVrrEnabledPropId, .value = 0}); |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/true); |
| HardwareDisplayPlaneList state; |
| |
| // Check initial VRR_ENABLED state. |
| { |
| DrmDevice::Property crtc_prop_vrr_enabled; |
| ScopedDrmObjectPropertyPtr crtc_props = |
| fake_drm_->GetObjectProperties(kCrtcIdBase, DRM_MODE_OBJECT_CRTC); |
| GetDrmPropertyForName(fake_drm_.get(), crtc_props.get(), "VRR_ENABLED", |
| &crtc_prop_vrr_enabled); |
| EXPECT_EQ(kVrrEnabledPropId, crtc_prop_vrr_enabled.id); |
| EXPECT_EQ(0U, crtc_prop_vrr_enabled.value); |
| } |
| |
| // Check VRR_ENABLED state is set by modeset. |
| { |
| CommitRequest commit_request; |
| DrmOverlayPlaneList overlays; |
| overlays.emplace_back(fake_buffer_, nullptr); |
| commit_request.push_back(CrtcCommitRequest::EnableCrtcRequest( |
| fake_drm_->crtc_property(0).id, fake_drm_->connector_property(0).id, |
| kDefaultMode, gfx::Point(), &state, std::move(overlays), |
| /*enable_vrr=*/true)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit( |
| std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| |
| DrmDevice::Property crtc_prop_vrr_enabled; |
| ScopedDrmObjectPropertyPtr crtc_props = |
| fake_drm_->GetObjectProperties(kCrtcIdBase, DRM_MODE_OBJECT_CRTC); |
| GetDrmPropertyForName(fake_drm_.get(), crtc_props.get(), "VRR_ENABLED", |
| &crtc_prop_vrr_enabled); |
| EXPECT_EQ(kVrrEnabledPropId, crtc_prop_vrr_enabled.id); |
| EXPECT_EQ(1U, crtc_prop_vrr_enabled.value); |
| } |
| |
| // Check VRR_ENABLED is reset by modeset. |
| { |
| CommitRequest commit_request; |
| DrmOverlayPlaneList overlays; |
| overlays.emplace_back(fake_buffer_, nullptr); |
| commit_request.push_back(CrtcCommitRequest::EnableCrtcRequest( |
| fake_drm_->crtc_property(0).id, fake_drm_->connector_property(0).id, |
| kDefaultMode, gfx::Point(), &state, std::move(overlays), |
| /*enable_vrr=*/false)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit( |
| std::move(commit_request), DRM_MODE_ATOMIC_ALLOW_MODESET)); |
| |
| DrmDevice::Property crtc_prop_vrr_enabled; |
| ScopedDrmObjectPropertyPtr crtc_props = |
| fake_drm_->GetObjectProperties(kCrtcIdBase, DRM_MODE_OBJECT_CRTC); |
| GetDrmPropertyForName(fake_drm_.get(), crtc_props.get(), "VRR_ENABLED", |
| &crtc_prop_vrr_enabled); |
| EXPECT_EQ(kVrrEnabledPropId, crtc_prop_vrr_enabled.id); |
| EXPECT_EQ(0U, crtc_prop_vrr_enabled.value); |
| } |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, MultiplePlaneAssignment) { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/2); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_EQ(2u, state_.plane_list.size()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, MultiplePlanesAndCrtcs) { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/2); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(1).id)); |
| EXPECT_EQ(4u, state_.plane_list.size()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, SharedPlanes) { |
| DrmOverlayPlaneList assigns; |
| scoped_refptr<DrmFramebuffer> buffer = CreateBuffer(gfx::Size(1, 1)); |
| |
| assigns.emplace_back(fake_buffer_, nullptr); |
| assigns.emplace_back(buffer, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/1); |
| |
| MockDrmDevice::PlaneProperties& plane_prop = |
| drm_state.plane_properties.emplace_back(); |
| plane_prop.id = 102; |
| plane_prop.crtc_mask = (1 << 0) | (1 << 1); |
| plane_prop.properties = { |
| {.id = kTypePropId, .value = DRM_PLANE_TYPE_OVERLAY}, |
| {.id = kInFormatsPropId, .value = kInFormatsBlobIdBase}, |
| }; |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(1).id)); |
| EXPECT_EQ(2u, state_.plane_list.size()); |
| // The shared plane is now unavailable for use by the other CRTC. |
| EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, UnusedPlanesAreReleased) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/2); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| DrmOverlayPlaneList assigns; |
| scoped_refptr<DrmFramebuffer> primary_buffer = |
| CreateBuffer(kDefaultBufferSize); |
| scoped_refptr<DrmFramebuffer> overlay_buffer = CreateBuffer(gfx::Size(1, 1)); |
| assigns.emplace_back(primary_buffer, nullptr); |
| assigns.emplace_back(overlay_buffer, nullptr); |
| HardwareDisplayPlaneList hdpl; |
| |
| scoped_refptr<PageFlipRequest> page_flip_request = |
| base::MakeRefCounted<PageFlipRequest>(base::TimeDelta()); |
| fake_drm_->plane_manager()->BeginFrame(&hdpl); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &hdpl, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_TRUE( |
| fake_drm_->plane_manager()->Commit(&hdpl, page_flip_request, nullptr)); |
| assigns.clear(); |
| assigns.emplace_back(primary_buffer, nullptr); |
| fake_drm_->plane_manager()->BeginFrame(&hdpl); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &hdpl, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset, "FB_ID")); |
| EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset + 1, "FB_ID")); |
| |
| EXPECT_TRUE( |
| fake_drm_->plane_manager()->Commit(&hdpl, page_flip_request, nullptr)); |
| EXPECT_NE(0u, GetPlanePropertyValue(kPlaneOffset, "FB_ID")); |
| EXPECT_EQ(0u, GetPlanePropertyValue(kPlaneOffset + 1, "FB_ID")); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, AssignPlanesRestoresInUse) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/2); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| DrmOverlayPlaneList assigns; |
| scoped_refptr<DrmFramebuffer> primary_buffer = |
| CreateBuffer(kDefaultBufferSize); |
| scoped_refptr<DrmFramebuffer> overlay_buffer = CreateBuffer(gfx::Size(1, 1)); |
| assigns.emplace_back(primary_buffer, nullptr); |
| assigns.emplace_back(overlay_buffer, nullptr); |
| HardwareDisplayPlaneList hdpl; |
| |
| scoped_refptr<PageFlipRequest> page_flip_request = |
| base::MakeRefCounted<PageFlipRequest>(base::TimeDelta()); |
| fake_drm_->plane_manager()->BeginFrame(&hdpl); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &hdpl, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_TRUE( |
| fake_drm_->plane_manager()->Commit(&hdpl, page_flip_request, nullptr)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->planes().front()->in_use()); |
| assigns.emplace_back(overlay_buffer, nullptr); |
| |
| fake_drm_->plane_manager()->BeginFrame(&hdpl); |
| // Assign overlay planes will fail since there aren't enough planes. |
| EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &hdpl, assigns, fake_drm_->crtc_property(0).id)); |
| |
| // The primary plane should still be in use since we failed to assign |
| // planes and did not commit a new configuration. |
| EXPECT_TRUE(fake_drm_->plane_manager()->planes().front()->in_use()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, PageflipTestRestoresInUse) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/2); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| DrmOverlayPlaneList assigns; |
| scoped_refptr<DrmFramebuffer> primary_buffer = |
| CreateBuffer(kDefaultBufferSize); |
| scoped_refptr<DrmFramebuffer> overlay_buffer = CreateBuffer(gfx::Size(1, 1)); |
| assigns.emplace_back(primary_buffer, nullptr); |
| assigns.emplace_back(overlay_buffer, nullptr); |
| HardwareDisplayPlaneList hdpl; |
| |
| scoped_refptr<PageFlipRequest> page_flip_request = |
| base::MakeRefCounted<PageFlipRequest>(base::TimeDelta()); |
| fake_drm_->plane_manager()->BeginFrame(&hdpl); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &hdpl, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_TRUE( |
| fake_drm_->plane_manager()->Commit(&hdpl, page_flip_request, nullptr)); |
| assigns.clear(); |
| fake_drm_->plane_manager()->BeginFrame(&hdpl); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &hdpl, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&hdpl, nullptr, nullptr)); |
| // The primary plane should still be in use since the commit was |
| // a pageflip test and did not change any KMS state. |
| EXPECT_TRUE(fake_drm_->plane_manager()->planes().front()->in_use()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, |
| PageFlipOnlySwapsPlaneListsOnSuccess) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/2); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| DrmOverlayPlaneList single_assign; |
| single_assign.emplace_back(CreateBuffer(kDefaultBufferSize), nullptr); |
| |
| DrmOverlayPlaneList overlay_assigns; |
| overlay_assigns.emplace_back(CreateBuffer(kDefaultBufferSize), nullptr); |
| overlay_assigns.emplace_back(CreateBuffer(kDefaultBufferSize), nullptr); |
| |
| HardwareDisplayPlaneList hdpl; |
| |
| auto flip_with_assigns = [&](bool commit_status, |
| const auto& assigns) -> bool { |
| auto page_flip_request = |
| base::MakeRefCounted<PageFlipRequest>(base::TimeDelta()); |
| fake_drm_->plane_manager()->BeginFrame(&hdpl); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &hdpl, assigns, fake_drm_->crtc_property(0).id)); |
| fake_drm_->set_commit_expectation(commit_status); |
| return fake_drm_->plane_manager()->Commit(&hdpl, page_flip_request, |
| nullptr); |
| }; |
| |
| // Flipping with an overlay should mark both as old planes: |
| EXPECT_TRUE(flip_with_assigns(/*commit_status=*/true, overlay_assigns)); |
| EXPECT_EQ(2u, hdpl.old_plane_list.size()); |
| EXPECT_EQ(0u, hdpl.plane_list.size()); |
| |
| // We shouldn't see a change to the old plane list on a force-failed commit, |
| // even though we only are trying to flip a single plane. |
| EXPECT_FALSE(flip_with_assigns(/*commit_status=*/false, single_assign)); |
| EXPECT_EQ(2u, hdpl.old_plane_list.size()); |
| EXPECT_EQ(0u, hdpl.plane_list.size()); |
| |
| // Once we do successfully flip a single plane, the old plane list should |
| // reflect it. |
| EXPECT_TRUE(flip_with_assigns(/*commit_status=*/true, single_assign)); |
| EXPECT_EQ(1u, hdpl.old_plane_list.size()); |
| EXPECT_EQ(0u, hdpl.plane_list.size()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, MultipleFrames) { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/2); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_EQ(1u, state_.plane_list.size()); |
| // Pretend we committed the frame. |
| state_.plane_list.swap(state_.old_plane_list); |
| fake_drm_->plane_manager()->BeginFrame(&state_); |
| HardwareDisplayPlane* old_plane = state_.old_plane_list[0]; |
| // The same plane should be used. |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_EQ(1u, state_.plane_list.size()); |
| EXPECT_EQ(state_.plane_list[0], old_plane); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, MultipleFramesDifferentPlanes) { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/2); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_EQ(1u, state_.plane_list.size()); |
| // The other plane should be used. |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| EXPECT_EQ(2u, state_.plane_list.size()); |
| EXPECT_NE(state_.plane_list[0], state_.plane_list[1]); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, PlanePinningAndUnpinning) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count*/ 2, |
| /*planes_per_crtc=*/1, |
| /*movable_planes=*/1); |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/true); |
| |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| DrmOverlayPlaneList assigns_with_overlay; |
| assigns_with_overlay.emplace_back(fake_buffer_, nullptr); |
| assigns_with_overlay.emplace_back(CreateBuffer(gfx::Size(1, 1)), nullptr); |
| |
| auto get_overlay_owner = [&]() { |
| for (const auto& plane : fake_drm_->plane_manager()->planes()) { |
| if (plane->type() == DRM_PLANE_TYPE_OVERLAY) { |
| return plane->owning_crtc(); |
| } |
| } |
| NOTREACHED(); |
| return UINT32_MAX; |
| }; |
| |
| uint32_t crtc_0 = fake_drm_->crtc_property(0).id; |
| uint32_t crtc_1 = fake_drm_->crtc_property(1).id; |
| HardwareDisplayPlaneList list_0; |
| HardwareDisplayPlaneList list_1; |
| |
| EXPECT_EQ(0u, get_overlay_owner()); |
| |
| PerformPageFlip(0, &list_0, assigns_with_overlay); |
| EXPECT_EQ(crtc_0, get_overlay_owner()) |
| << "Assigning a plane should pin it to the CRTC."; |
| fake_drm_->RunCallbacks(); |
| |
| EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &list_1, assigns_with_overlay, crtc_1)) |
| << "Pinned planes should be unassignable while they're pinned."; |
| |
| PerformPageFlip(0, &list_0, assigns); |
| EXPECT_EQ(0u, get_overlay_owner()) |
| << "Assigning without overlays should unpin the overlay."; |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &list_1, assigns_with_overlay, crtc_1)) |
| << "Previously pinned planes should be available for use after " |
| "unpinning."; |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, PlanesUnpinnedOnFailedFlip) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count*/ 2, |
| /*planes_per_crtc=*/1, |
| /*movable_planes=*/1); |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/true); |
| |
| DrmOverlayPlaneList assigns_with_overlay; |
| assigns_with_overlay.emplace_back(fake_buffer_, nullptr); |
| assigns_with_overlay.emplace_back(CreateBuffer(gfx::Size(1, 1)), nullptr); |
| |
| auto get_overlay_owner = [&]() { |
| for (const auto& plane : fake_drm_->plane_manager()->planes()) { |
| if (plane->type() == DRM_PLANE_TYPE_OVERLAY) { |
| return plane->owning_crtc(); |
| } |
| } |
| NOTREACHED(); |
| return UINT32_MAX; |
| }; |
| |
| uint32_t crtc_0 = fake_drm_->crtc_property(0).id; |
| HardwareDisplayPlaneList hdpl; |
| |
| EXPECT_EQ(0u, get_overlay_owner()); |
| |
| PerformPageFlip(0, &hdpl, assigns_with_overlay); |
| EXPECT_EQ(crtc_0, get_overlay_owner()) |
| << "Assigning a plane should pin it to the CRTC."; |
| fake_drm_->RunCallbacks(); |
| |
| PerformFailingPageFlip(0, &hdpl, assigns_with_overlay); |
| EXPECT_EQ(0u, get_overlay_owner()) |
| << "A failed flip should result in the overlay being freed again."; |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, PlanesUnpinnedOnDisable) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count*/ 2, |
| /*planes_per_crtc=*/1, |
| /*movable_planes=*/1); |
| fake_drm_->InitializeState(drm_state, /*use_atomic=*/true); |
| |
| DrmOverlayPlaneList assigns_with_overlay; |
| assigns_with_overlay.emplace_back(fake_buffer_, nullptr); |
| assigns_with_overlay.emplace_back(CreateBuffer(gfx::Size(1, 1)), nullptr); |
| |
| auto get_overlay_owner = [&]() { |
| for (const auto& plane : fake_drm_->plane_manager()->planes()) { |
| if (plane->type() == DRM_PLANE_TYPE_OVERLAY) { |
| return plane->owning_crtc(); |
| } |
| } |
| NOTREACHED(); |
| return UINT32_MAX; |
| }; |
| |
| uint32_t crtc_0 = fake_drm_->crtc_property(0).id; |
| HardwareDisplayPlaneList hdpl; |
| |
| EXPECT_EQ(0u, get_overlay_owner()); |
| |
| PerformPageFlip(0, &hdpl, assigns_with_overlay); |
| EXPECT_EQ(crtc_0, get_overlay_owner()) |
| << "Assigning a plane should pin it to the CRTC."; |
| fake_drm_->RunCallbacks(); |
| |
| fake_drm_->plane_manager()->DisableOverlayPlanes(&hdpl); |
| EXPECT_EQ(0u, get_overlay_owner()) |
| << "After disabling, the pinned overlay owner should be reset."; |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, |
| SetColorCorrectionOnAllCrtcPlanes_Success) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| drm_state.plane_properties[0].properties.push_back( |
| {.id = kPlaneCtmId, .value = 0}); |
| drm_state.plane_properties[1].properties.push_back( |
| {.id = kPlaneCtmId, .value = 0}); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| ScopedDrmColorCtmPtr ctm_blob(CreateCTMBlob(std::vector<float>(9))); |
| EXPECT_TRUE(fake_drm_->plane_manager()->SetColorCorrectionOnAllCrtcPlanes( |
| fake_drm_->crtc_property(0).id, std::move(ctm_blob))); |
| EXPECT_EQ(1, fake_drm_->get_commit_count()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, |
| SetColorCorrectionOnAllCrtcPlanes_NoPlaneCtmProperty) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| ScopedDrmColorCtmPtr ctm_blob(CreateCTMBlob(std::vector<float>(9))); |
| EXPECT_FALSE(fake_drm_->plane_manager()->SetColorCorrectionOnAllCrtcPlanes( |
| fake_drm_->crtc_property(0).id, std::move(ctm_blob))); |
| EXPECT_EQ(0, fake_drm_->get_commit_count()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, |
| SetColorCorrectionOnAllCrtcPlanes_OnePlaneMissingCtmProperty) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/2); |
| drm_state.plane_properties[0].properties.push_back( |
| {.id = kPlaneCtmId, .value = 0}); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| ScopedDrmColorCtmPtr ctm_blob(CreateCTMBlob(std::vector<float>(9))); |
| EXPECT_FALSE(fake_drm_->plane_manager()->SetColorCorrectionOnAllCrtcPlanes( |
| fake_drm_->crtc_property(0).id, std::move(ctm_blob))); |
| EXPECT_EQ(0, fake_drm_->get_commit_count()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerTest, SetColorMatrix_Success) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kCtmPropId, .value = 0}); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->SetColorMatrix( |
| fake_drm_->crtc_property(0).id, std::vector<float>(9))); |
| if (use_atomic_) { |
| HardwareDisplayPlaneList state; |
| PerformPageFlip(/*crtc_idx=*/0, &state); |
| #if defined(COMMIT_PROPERTIES_ON_PAGE_FLIP) |
| EXPECT_EQ(1, fake_drm_->get_commit_count()); |
| #else |
| EXPECT_EQ(2, fake_drm_->get_commit_count()); |
| #endif |
| EXPECT_NE(0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, "CTM")); |
| } else { |
| EXPECT_EQ(1, fake_drm_->get_set_object_property_count()); |
| } |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerTest, SetColorMatrix_ErrorEmptyCtm) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kCtmPropId, .value = 0}); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_FALSE(fake_drm_->plane_manager()->SetColorMatrix( |
| fake_drm_->crtc_property(0).id, {})); |
| if (use_atomic_) { |
| HardwareDisplayPlaneList state; |
| PerformPageFlip(/*crtc_idx=*/0, &state); |
| EXPECT_EQ(1, fake_drm_->get_commit_count()); |
| EXPECT_EQ(0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, "CTM")); |
| } else { |
| EXPECT_EQ(0, fake_drm_->get_set_object_property_count()); |
| } |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_MissingDegamma) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kCtmPropId, .value = 0}); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_FALSE(fake_drm_->plane_manager()->SetGammaCorrection( |
| fake_drm_->crtc_property(0).id, kNonemptyGammaCurve, kEmptyGammaCurve)); |
| if (use_atomic_) { |
| HardwareDisplayPlaneList state; |
| PerformPageFlip(/*crtc_idx=*/0, &state); |
| // Page flip should succeed even if the properties failed to be updated. |
| EXPECT_EQ(1, fake_drm_->get_commit_count()); |
| } else { |
| EXPECT_EQ(0, fake_drm_->get_set_object_property_count()); |
| } |
| |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kDegammaLutSizePropId, .value = 1}); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_FALSE(fake_drm_->plane_manager()->SetGammaCorrection( |
| fake_drm_->crtc_property(0).id, kNonemptyGammaCurve, kEmptyGammaCurve)); |
| if (use_atomic_) { |
| HardwareDisplayPlaneList state; |
| PerformPageFlip(/*crtc_idx=*/0, &state); |
| // Page flip should succeed even if the properties failed to be updated. |
| EXPECT_EQ(2, fake_drm_->get_commit_count()); |
| } else { |
| EXPECT_EQ(0, fake_drm_->get_set_object_property_count()); |
| } |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_MissingGamma) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kCtmPropId, .value = 0}); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_FALSE(fake_drm_->plane_manager()->SetGammaCorrection( |
| fake_drm_->crtc_property(0).id, kEmptyGammaCurve, kNonemptyGammaCurve)); |
| if (use_atomic_) { |
| HardwareDisplayPlaneList state; |
| PerformPageFlip(/*crtc_idx=*/0, &state); |
| // Page flip should succeed even if the properties failed to be updated. |
| EXPECT_EQ(1, fake_drm_->get_commit_count()); |
| } else { |
| EXPECT_EQ(0, fake_drm_->get_set_object_property_count()); |
| } |
| |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kGammaLutSizePropId, .value = 1}); |
| |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_FALSE(fake_drm_->plane_manager()->SetGammaCorrection( |
| fake_drm_->crtc_property(0).id, kEmptyGammaCurve, kNonemptyGammaCurve)); |
| if (use_atomic_) { |
| HardwareDisplayPlaneList state; |
| PerformPageFlip(/*crtc_idx=*/0, &state); |
| // Page flip should succeed even if the properties failed to be updated. |
| EXPECT_EQ(2, fake_drm_->get_commit_count()); |
| } else { |
| EXPECT_EQ(0, fake_drm_->get_set_object_property_count()); |
| } |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_LegacyGamma) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| fake_drm_->set_legacy_gamma_ramp_expectation(true); |
| EXPECT_TRUE(fake_drm_->plane_manager()->SetGammaCorrection( |
| fake_drm_->crtc_property(0).id, kEmptyGammaCurve, kNonemptyGammaCurve)); |
| EXPECT_EQ(1, fake_drm_->get_set_gamma_ramp_count()); |
| EXPECT_EQ(0, fake_drm_->get_commit_count()); |
| EXPECT_EQ(0, fake_drm_->get_set_object_property_count()); |
| |
| // Ensure disabling gamma also works on legacy. |
| EXPECT_TRUE(fake_drm_->plane_manager()->SetGammaCorrection( |
| fake_drm_->crtc_property(0).id, kEmptyGammaCurve, kEmptyGammaCurve)); |
| EXPECT_EQ(2, fake_drm_->get_set_gamma_ramp_count()); |
| EXPECT_EQ(0, fake_drm_->get_commit_count()); |
| EXPECT_EQ(0, fake_drm_->get_set_object_property_count()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_Success) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kCtmPropId, .value = 0}); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| EXPECT_FALSE(fake_drm_->plane_manager()->SetGammaCorrection( |
| fake_drm_->crtc_property(0).id, kNonemptyGammaCurve, kEmptyGammaCurve)); |
| EXPECT_EQ(0, fake_drm_->get_commit_count()); |
| |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kDegammaLutSizePropId, .value = 1}); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kDegammaLutPropId, .value = 0}); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kGammaLutSizePropId, .value = 1}); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kGammaLutPropId, .value = 0}); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| HardwareDisplayPlaneList state; |
| // Check that we reset the properties correctly. |
| EXPECT_TRUE(fake_drm_->plane_manager()->SetGammaCorrection( |
| fake_drm_->crtc_property(0).id, {}, {})); |
| if (use_atomic_) { |
| PerformPageFlip(/*crtc_idx=*/0, &state); |
| #if defined(COMMIT_PROPERTIES_ON_PAGE_FLIP) |
| EXPECT_EQ(1, fake_drm_->get_commit_count()); |
| #else |
| EXPECT_EQ(2, fake_drm_->get_commit_count()); |
| #endif |
| EXPECT_EQ( |
| 0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, "GAMMA_LUT")); |
| EXPECT_EQ(0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, |
| "DEGAMMA_LUT")); |
| } else { |
| EXPECT_EQ(2, fake_drm_->get_set_object_property_count()); |
| } |
| |
| EXPECT_TRUE(fake_drm_->plane_manager()->SetGammaCorrection( |
| fake_drm_->crtc_property(0).id, kNonemptyGammaCurve, |
| kNonemptyGammaCurve)); |
| if (use_atomic_) { |
| PerformPageFlip(/*crtc_idx=*/0, &state); |
| #if defined(COMMIT_PROPERTIES_ON_PAGE_FLIP) |
| EXPECT_EQ(2, fake_drm_->get_commit_count()); |
| #else |
| EXPECT_EQ(4, fake_drm_->get_commit_count()); |
| #endif |
| EXPECT_NE( |
| 0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, "GAMMA_LUT")); |
| EXPECT_NE(0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, |
| "DEGAMMA_LUT")); |
| } else { |
| EXPECT_EQ(4, fake_drm_->get_set_object_property_count()); |
| } |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerTest, SetBackgroundColor_Success) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kBackgroundColorPropId, .value = 0}); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| fake_drm_->plane_manager()->SetBackgroundColor(fake_drm_->crtc_property(0).id, |
| 0); |
| if (use_atomic_) { |
| HardwareDisplayPlaneList state; |
| PerformPageFlip(/*crtc_idx=*/0, &state); |
| EXPECT_EQ(1, fake_drm_->get_commit_count()); |
| EXPECT_EQ(0u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, |
| "BACKGROUND_COLOR")); |
| } else { |
| EXPECT_EQ(0, fake_drm_->get_set_object_property_count()); |
| } |
| |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kBackgroundColorPropId, .value = 1}); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| fake_drm_->plane_manager()->SetBackgroundColor(fake_drm_->crtc_property(0).id, |
| 1); |
| if (use_atomic_) { |
| HardwareDisplayPlaneList state; |
| PerformPageFlip(/*crtc_idx=*/0, &state); |
| EXPECT_EQ(2, fake_drm_->get_commit_count()); |
| EXPECT_EQ(1u, GetCrtcPropertyValue(fake_drm_->crtc_property(0).id, |
| "BACKGROUND_COLOR")); |
| } else { |
| EXPECT_EQ(0, fake_drm_->get_set_object_property_count()); |
| } |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, |
| CommitReturnsNullOutFenceIfOutFencePtrNotSupported) { |
| scoped_refptr<DrmFramebuffer> fake_buffer2 = CreateBuffer(kDefaultBufferSize); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/2, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| DrmOverlayPlaneList assigns1; |
| assigns1.emplace_back(fake_buffer_, nullptr); |
| DrmOverlayPlaneList assigns2; |
| assigns2.emplace_back(fake_buffer2, nullptr); |
| |
| fake_drm_->plane_manager()->BeginFrame(&state_); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns1, fake_drm_->crtc_property(0).id)); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns2, fake_drm_->crtc_property(1).id)); |
| |
| scoped_refptr<PageFlipRequest> page_flip_request = |
| base::MakeRefCounted<PageFlipRequest>(base::TimeDelta()); |
| |
| gfx::GpuFenceHandle release_fence; |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&state_, page_flip_request, |
| &release_fence)); |
| EXPECT_TRUE(release_fence.is_null()); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerTest, |
| InitializationFailsIfSupportForOutFencePropertiesIsPartial) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/3, /*planes_per_crtc=*/1); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kOutFencePtrPropId, .value = 1}); |
| drm_state.crtc_properties[2].properties.push_back( |
| {.id = kOutFencePtrPropId, .value = 2}); |
| |
| EXPECT_FALSE(fake_drm_->InitializeStateWithResult(drm_state, use_atomic_)); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerTest, |
| InitializationSucceedsIfSupportForOutFencePropertiesIsComplete) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/3, /*planes_per_crtc=*/1); |
| drm_state.crtc_properties[0].properties.push_back( |
| {.id = kOutFencePtrPropId, .value = 1}); |
| drm_state.crtc_properties[1].properties.push_back( |
| {.id = kOutFencePtrPropId, .value = 2}); |
| drm_state.crtc_properties[2].properties.push_back( |
| {.id = kOutFencePtrPropId, .value = 3}); |
| |
| EXPECT_TRUE(fake_drm_->InitializeStateWithResult(drm_state, use_atomic_)); |
| } |
| |
| // Verifies that formats with 2 bits of alpha decay to opaques for AddFB2(). |
| TEST_P(HardwareDisplayPlaneManagerTest, ForceOpaqueFormatsForAddFramebuffer) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/3, /*planes_per_crtc=*/1); |
| |
| struct { |
| uint32_t input_fourcc; // FourCC presented to AddFramebuffer. |
| uint32_t used_fourcc; // FourCC expected to be used in AddFramebuffer. |
| } kFourCCFormats[] = { |
| {DRM_FORMAT_ABGR2101010, DRM_FORMAT_XBGR2101010}, |
| {DRM_FORMAT_ARGB2101010, DRM_FORMAT_XRGB2101010}, |
| }; |
| |
| for (const auto& format_pair : kFourCCFormats) { |
| scoped_refptr<DrmFramebuffer> drm_fb = |
| CreateBufferWithFormat(kDefaultBufferSize, format_pair.input_fourcc); |
| |
| EXPECT_EQ(drm_fb->framebuffer_pixel_format(), format_pair.used_fourcc); |
| EXPECT_EQ(drm_fb->opaque_framebuffer_pixel_format(), |
| format_pair.used_fourcc); |
| } |
| |
| // If DRM supports high-bitdepth formats with Alpha, there's no need for |
| // opaque decaying. Note that we have to support all |kFourCCFormats|. |
| fake_drm_->SetPropertyBlob(MockDrmDevice::AllocateInFormatsBlob( |
| kInFormatsBlobIdBase, {DRM_FORMAT_ARGB2101010, DRM_FORMAT_ABGR2101010}, |
| {})); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| for (const auto& format_pair : kFourCCFormats) { |
| scoped_refptr<DrmFramebuffer> drm_fb = |
| CreateBufferWithFormat(kDefaultBufferSize, format_pair.input_fourcc); |
| |
| EXPECT_EQ(drm_fb->framebuffer_pixel_format(), format_pair.input_fourcc); |
| EXPECT_EQ(drm_fb->opaque_framebuffer_pixel_format(), |
| format_pair.used_fourcc); |
| } |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerTest, GetHardwareCapabilities) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/4, /*planes_per_crtc=*/7); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| for (int i = 0; i < 4; ++i) { |
| auto hc = |
| fake_drm_->plane_manager()->GetHardwareCapabilities(kCrtcIdBase + i); |
| EXPECT_TRUE(hc.is_valid); |
| // Legacy doesn't support OVERLAY planes. |
| int expected_planes = use_atomic_ ? 7 : 1; |
| EXPECT_EQ(hc.num_overlay_capable_planes, expected_planes); |
| } |
| |
| { |
| // Change the last (CURSOR) plane into a PRIMARY plane that is available to |
| // only the first two CRTCs. |
| auto& last_props = |
| drm_state.plane_properties[drm_state.plane_properties.size() - 1]; |
| last_props.crtc_mask = (1 << 0) | (1 << 1); |
| // Find the type property and change it to PRIMARY. |
| for (auto& property : last_props.properties) { |
| if (property.id == kTypePropId) { |
| property.value = DRM_PLANE_TYPE_PRIMARY; |
| break; |
| } |
| } |
| |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| } |
| |
| for (int i = 0; i < 4; ++i) { |
| auto hc = |
| fake_drm_->plane_manager()->GetHardwareCapabilities(kCrtcIdBase + i); |
| |
| EXPECT_TRUE(hc.is_valid); |
| // Legacy doesn't support OVERLAY planes. |
| int expected_planes = use_atomic_ ? 7 : 1; |
| // First two CRTCs have the newly added plane available. |
| if (i == 0 || i == 1) { |
| expected_planes++; |
| } |
| EXPECT_EQ(hc.num_overlay_capable_planes, expected_planes); |
| } |
| |
| { |
| fake_drm_->SetDriverName(absl::nullopt); |
| auto hc = fake_drm_->plane_manager()->GetHardwareCapabilities(kCrtcIdBase); |
| EXPECT_FALSE(hc.is_valid); |
| |
| fake_drm_->SetDriverName("amdgpu"); |
| hc = fake_drm_->plane_manager()->GetHardwareCapabilities(kCrtcIdBase); |
| EXPECT_TRUE(hc.is_valid); |
| EXPECT_FALSE(hc.has_independent_cursor_plane); |
| |
| fake_drm_->SetDriverName("generic"); |
| hc = fake_drm_->plane_manager()->GetHardwareCapabilities(kCrtcIdBase); |
| EXPECT_TRUE(hc.is_valid); |
| EXPECT_TRUE(hc.has_independent_cursor_plane); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| HardwareDisplayPlaneManagerTest, |
| testing::Values(false, true)); |
| |
| // TODO(dnicoara): Migrate as many tests as possible to the general list above. |
| INSTANTIATE_TEST_SUITE_P(All, |
| HardwareDisplayPlaneManagerLegacyTest, |
| testing::Values(false)); |
| |
| INSTANTIATE_TEST_SUITE_P(All, |
| HardwareDisplayPlaneManagerAtomicTest, |
| testing::Values(true)); |
| |
| class FakeFenceFD { |
| public: |
| FakeFenceFD(); |
| |
| std::unique_ptr<gfx::GpuFence> GetGpuFence() const; |
| void Signal() const; |
| |
| private: |
| base::ScopedFD read_fd; |
| base::ScopedFD write_fd; |
| }; |
| |
| FakeFenceFD::FakeFenceFD() { |
| int fds[2]; |
| base::CreateLocalNonBlockingPipe(fds); |
| read_fd = base::ScopedFD(fds[0]); |
| write_fd = base::ScopedFD(fds[1]); |
| } |
| |
| std::unique_ptr<gfx::GpuFence> FakeFenceFD::GetGpuFence() const { |
| gfx::GpuFenceHandle handle; |
| handle.Adopt(base::ScopedFD(HANDLE_EINTR(dup(read_fd.get())))); |
| return std::make_unique<gfx::GpuFence>(std::move(handle)); |
| } |
| |
| void FakeFenceFD::Signal() const { |
| base::WriteFileDescriptor(write_fd.get(), "a"); |
| } |
| |
| class HardwareDisplayPlaneManagerPlanesReadyTest : public testing::Test { |
| public: |
| HardwareDisplayPlaneManagerPlanesReadyTest( |
| const HardwareDisplayPlaneManagerPlanesReadyTest&) = delete; |
| HardwareDisplayPlaneManagerPlanesReadyTest& operator=( |
| const HardwareDisplayPlaneManagerPlanesReadyTest&) = delete; |
| |
| protected: |
| HardwareDisplayPlaneManagerPlanesReadyTest() = default; |
| |
| void SetUp() override { |
| auto gbm_device = std::make_unique<MockGbmDevice>(); |
| fake_drm_ = new MockDrmDevice(std::move(gbm_device)); |
| drm_framebuffer_ = CreateBuffer(kDefaultBufferSize); |
| planes_without_fences_ = CreatePlanesWithoutFences(); |
| planes_with_fences_ = CreatePlanesWithFences(); |
| } |
| |
| void UseLegacyManager(); |
| void UseAtomicManager(); |
| void RequestPlanesReady(DrmOverlayPlaneList planes); |
| |
| scoped_refptr<DrmFramebuffer> CreateBuffer(const gfx::Size& size) { |
| std::unique_ptr<GbmBuffer> buffer = fake_drm_->gbm_device()->CreateBuffer( |
| DRM_FORMAT_XRGB8888, size, GBM_BO_USE_SCANOUT); |
| return DrmFramebuffer::AddFramebuffer(fake_drm_, buffer.get(), size); |
| } |
| |
| DrmOverlayPlaneList CreatePlanesWithoutFences() { |
| DrmOverlayPlaneList planes; |
| planes.emplace_back(CreateBuffer(kDefaultBufferSize), nullptr); |
| planes.emplace_back(CreateBuffer(kDefaultBufferSize), nullptr); |
| return planes; |
| } |
| |
| DrmOverlayPlaneList CreatePlanesWithFences() { |
| DrmOverlayPlaneList planes; |
| planes.emplace_back(CreateBuffer(kDefaultBufferSize), |
| fake_fence_fd1_.GetGpuFence()); |
| planes.emplace_back(CreateBuffer(kDefaultBufferSize), |
| fake_fence_fd2_.GetGpuFence()); |
| return planes; |
| } |
| |
| scoped_refptr<MockDrmDevice> fake_drm_; |
| std::unique_ptr<HardwareDisplayPlaneManager> plane_manager_; |
| bool callback_called = false; |
| base::test::TaskEnvironment task_env_{ |
| base::test::TaskEnvironment::MainThreadType::DEFAULT, |
| base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED}; |
| scoped_refptr<DrmFramebuffer> drm_framebuffer_; |
| const FakeFenceFD fake_fence_fd1_; |
| const FakeFenceFD fake_fence_fd2_; |
| |
| DrmOverlayPlaneList planes_without_fences_; |
| DrmOverlayPlaneList planes_with_fences_; |
| }; |
| |
| void HardwareDisplayPlaneManagerPlanesReadyTest::RequestPlanesReady( |
| DrmOverlayPlaneList planes) { |
| auto set_true = [](bool* b, DrmOverlayPlaneList planes) { *b = true; }; |
| plane_manager_->RequestPlanesReadyCallback( |
| std::move(planes), base::BindOnce(set_true, &callback_called)); |
| } |
| |
| void HardwareDisplayPlaneManagerPlanesReadyTest::UseLegacyManager() { |
| plane_manager_ = |
| std::make_unique<HardwareDisplayPlaneManagerLegacy>(fake_drm_.get()); |
| } |
| |
| void HardwareDisplayPlaneManagerPlanesReadyTest::UseAtomicManager() { |
| plane_manager_ = |
| std::make_unique<HardwareDisplayPlaneManagerAtomic>(fake_drm_.get()); |
| } |
| |
| TEST_F(HardwareDisplayPlaneManagerPlanesReadyTest, |
| LegacyWithoutFencesIsAsynchronousWithoutFenceWait) { |
| UseLegacyManager(); |
| RequestPlanesReady(DrmOverlayPlane::Clone(planes_without_fences_)); |
| |
| EXPECT_FALSE(callback_called); |
| |
| task_env_.RunUntilIdle(); |
| |
| EXPECT_TRUE(callback_called); |
| } |
| |
| TEST_F(HardwareDisplayPlaneManagerPlanesReadyTest, |
| LegacyWithFencesIsAsynchronousWithFenceWait) { |
| UseLegacyManager(); |
| RequestPlanesReady(DrmOverlayPlane::Clone(planes_with_fences_)); |
| |
| EXPECT_FALSE(callback_called); |
| |
| fake_fence_fd1_.Signal(); |
| fake_fence_fd2_.Signal(); |
| |
| EXPECT_FALSE(callback_called); |
| |
| task_env_.RunUntilIdle(); |
| |
| EXPECT_TRUE(callback_called); |
| } |
| |
| TEST_F(HardwareDisplayPlaneManagerPlanesReadyTest, |
| AtomicWithoutFencesIsAsynchronousWithoutFenceWait) { |
| UseAtomicManager(); |
| RequestPlanesReady(DrmOverlayPlane::Clone(planes_without_fences_)); |
| |
| EXPECT_FALSE(callback_called); |
| |
| task_env_.RunUntilIdle(); |
| |
| EXPECT_TRUE(callback_called); |
| } |
| |
| TEST_F(HardwareDisplayPlaneManagerPlanesReadyTest, |
| AtomicWithFencesIsAsynchronousWithoutFenceWait) { |
| UseAtomicManager(); |
| RequestPlanesReady(DrmOverlayPlane::Clone(planes_with_fences_)); |
| |
| EXPECT_FALSE(callback_called); |
| |
| task_env_.RunUntilIdle(); |
| |
| EXPECT_TRUE(callback_called); |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, OriginalModifiersSupportOnly) { |
| fake_drm_->SetPropertyBlob(MockDrmDevice::AllocateInFormatsBlob( |
| kInFormatsBlobIdBase, {DRM_FORMAT_NV12}, {})); |
| |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| { |
| DrmOverlayPlaneList assigns; |
| // Create as NV12 since this is required for rotation support. |
| std::unique_ptr<GbmBuffer> buffer = fake_drm_->gbm_device()->CreateBuffer( |
| DRM_FORMAT_NV12, kDefaultBufferSize, GBM_BO_USE_SCANOUT); |
| scoped_refptr<DrmFramebuffer> framebuffer_original = |
| DrmFramebuffer::AddFramebuffer(fake_drm_, buffer.get(), |
| kDefaultBufferSize, {}, true); |
| assigns.emplace_back(framebuffer_original, nullptr); |
| assigns.back().plane_transform = gfx::OVERLAY_TRANSFORM_ROTATE_270; |
| |
| fake_drm_->plane_manager()->BeginFrame(&state_); |
| // Rotation should be supported for this buffer as it is the original buffer |
| // with the original modifiers. |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| |
| gfx::GpuFenceHandle release_fence; |
| scoped_refptr<PageFlipRequest> page_flip_request = |
| base::MakeRefCounted<PageFlipRequest>(base::TimeDelta()); |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&state_, page_flip_request, |
| &release_fence)); |
| } |
| |
| { |
| DrmOverlayPlaneList assigns; |
| assigns.clear(); |
| fake_drm_->plane_manager()->BeginFrame(&state_); |
| // The test buffer would not have accurate modifiers and therefore should |
| // fail rotation. |
| std::unique_ptr<GbmBuffer> buffer = fake_drm_->gbm_device()->CreateBuffer( |
| DRM_FORMAT_NV12, kDefaultBufferSize, GBM_BO_USE_SCANOUT); |
| scoped_refptr<DrmFramebuffer> framebuffer_non_original = |
| DrmFramebuffer::AddFramebuffer(fake_drm_, buffer.get(), |
| kDefaultBufferSize, {}, false); |
| assigns.emplace_back(framebuffer_non_original, nullptr); |
| assigns.back().plane_transform = gfx::OVERLAY_TRANSFORM_ROTATE_270; |
| EXPECT_FALSE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| } |
| } |
| |
| TEST_P(HardwareDisplayPlaneManagerAtomicTest, OverlaySourceCrop) { |
| auto drm_state = MockDrmDevice::MockDrmState::CreateStateWithDefaultObjects( |
| /*crtc_count=*/1, /*planes_per_crtc=*/1); |
| fake_drm_->InitializeState(drm_state, use_atomic_); |
| |
| { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, nullptr); |
| |
| fake_drm_->plane_manager()->BeginFrame(&state_); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| |
| gfx::GpuFenceHandle release_fence; |
| scoped_refptr<PageFlipRequest> page_flip_request = |
| base::MakeRefCounted<PageFlipRequest>(base::TimeDelta()); |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&state_, page_flip_request, |
| &release_fence)); |
| |
| EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_W")); |
| EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_H")); |
| } |
| |
| { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, 0, |
| gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, |
| gfx::Rect(), gfx::Rect(kDefaultBufferSize), |
| gfx::RectF(0, 0, .5, 1), false, nullptr); |
| |
| fake_drm_->plane_manager()->BeginFrame(&state_); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| |
| scoped_refptr<PageFlipRequest> page_flip_request = |
| base::MakeRefCounted<PageFlipRequest>(base::TimeDelta()); |
| gfx::GpuFenceHandle release_fence; |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&state_, page_flip_request, |
| &release_fence)); |
| |
| EXPECT_EQ(1u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_W")); |
| EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_H")); |
| } |
| |
| { |
| DrmOverlayPlaneList assigns; |
| assigns.emplace_back(fake_buffer_, 0, |
| gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, |
| gfx::Rect(), gfx::Rect(kDefaultBufferSize), |
| gfx::RectF(0, 0, .999, .501), false, nullptr); |
| |
| fake_drm_->plane_manager()->BeginFrame(&state_); |
| EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes( |
| &state_, assigns, fake_drm_->crtc_property(0).id)); |
| |
| scoped_refptr<PageFlipRequest> page_flip_request = |
| base::MakeRefCounted<PageFlipRequest>(base::TimeDelta()); |
| gfx::GpuFenceHandle release_fence; |
| EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&state_, page_flip_request, |
| &release_fence)); |
| |
| EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_W")); |
| EXPECT_EQ(1u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_H")); |
| } |
| } |
| |
| class HardwareDisplayPlaneAtomicMock : public HardwareDisplayPlaneAtomic { |
| public: |
| HardwareDisplayPlaneAtomicMock() : HardwareDisplayPlaneAtomic(1) {} |
| ~HardwareDisplayPlaneAtomicMock() override = default; |
| |
| bool AssignPlaneProps(DrmDevice* drm, |
| uint32_t crtc_id, |
| uint32_t framebuffer, |
| const gfx::Rect& crtc_rect, |
| const gfx::Rect& src_rect, |
| const gfx::Rect& damage_rect, |
| const gfx::OverlayTransform transform, |
| int in_fence_fd, |
| uint32_t format_fourcc, |
| bool is_original_buffer) override { |
| framebuffer_ = framebuffer; |
| return true; |
| } |
| uint32_t framebuffer() const { return framebuffer_; } |
| |
| private: |
| uint32_t framebuffer_ = 0; |
| }; |
| |
| TEST(HardwareDisplayPlaneManagerAtomic, EnableBlend) { |
| auto gbm_device = std::make_unique<MockGbmDevice>(); |
| auto drm_device = base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device)); |
| auto plane_manager = |
| std::make_unique<HardwareDisplayPlaneManagerAtomic>(drm_device.get()); |
| HardwareDisplayPlaneList plane_list; |
| HardwareDisplayPlaneAtomicMock hw_plane; |
| std::unique_ptr<GbmBuffer> buffer = drm_device->gbm_device()->CreateBuffer( |
| DRM_FORMAT_XRGB8888, kDefaultBufferSize, GBM_BO_USE_SCANOUT); |
| scoped_refptr<DrmFramebuffer> framebuffer = DrmFramebuffer::AddFramebuffer( |
| drm_device, buffer.get(), kDefaultBufferSize); |
| DrmOverlayPlane overlay(framebuffer, nullptr); |
| overlay.enable_blend = true; |
| plane_manager->SetPlaneData(&plane_list, &hw_plane, overlay, 1, gfx::Rect()); |
| EXPECT_EQ(hw_plane.framebuffer(), framebuffer->framebuffer_id()); |
| |
| overlay.enable_blend = false; |
| plane_manager->SetPlaneData(&plane_list, &hw_plane, overlay, 1, gfx::Rect()); |
| EXPECT_EQ(hw_plane.framebuffer(), framebuffer->opaque_framebuffer_id()); |
| } |
| |
| } // namespace ui |