Commit 29765fea authored by David Stevens's avatar David Stevens Committed by Commit Bot

ui/ozone/drm: don't use subpixel source crop

DRM supposedly supports subpixel source crop. However, according to
drm_plane_funcs.update_plane, devices which don't support that are free
to ignore the fractional part, and every device seems to do that as of
5.4. To deal with this, round source crops that are nearly integers and
reject overlay candidates that actually require subpixel source crop.

The rounding of nearly integer crops addresses an issue where floating
point imprecision in a quad's uv_crop could lead to a visible
difference depending on whether or not the quad was put in an overlay.
The visible difference arose because drivers truncate the fractional
part of the source crop, which could lead to a 1px discrepancy in the
source crop.

BUG=chromium:1099110

Change-Id: Ib20b16ef505574bd63502591db626a7e769f6e88
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2366397
Commit-Queue: David Stevens <stevensd@chromium.org>
Reviewed-by: default avatarDaniele Castagna <dcastagna@chromium.org>
Cr-Commit-Position: refs/heads/master@{#801234}
parent 5caa033e
...@@ -149,6 +149,15 @@ bool DrmOverlayManager::CanHandleCandidate( ...@@ -149,6 +149,15 @@ bool DrmOverlayManager::CanHandleCandidate(
if (!gfx::IsNearestRectWithinDistance(candidate.display_rect, 0.01f)) if (!gfx::IsNearestRectWithinDistance(candidate.display_rect, 0.01f))
return false; return false;
// DRM supposedly supports subpixel source crop. However, according to
// drm_plane_funcs.update_plane, devices which don't support that are
// free to ignore the fractional part, and every device seems to do that as
// of 5.4. So reject candidates that require subpixel source crop.
gfx::RectF crop(candidate.crop_rect);
crop.Scale(candidate.buffer_size.width(), candidate.buffer_size.height());
if (!gfx::IsNearestRectWithinDistance(crop, 0.01f))
return false;
if (candidate.is_clipped && !candidate.clip_rect.Contains( if (candidate.is_clipped && !candidate.clip_rect.Contains(
gfx::ToNearestRect(candidate.display_rect))) { gfx::ToNearestRect(candidate.display_rect))) {
return false; return false;
......
...@@ -30,8 +30,8 @@ ...@@ -30,8 +30,8 @@
namespace { namespace {
// Mode of size 6x4. // Mode of size 12x8.
const drmModeModeInfo kDefaultMode = {0, 6, 0, 0, 0, 0, 4, 0, const drmModeModeInfo kDefaultMode = {0, 12, 0, 0, 0, 0, 8, 0,
0, 0, 0, 0, 0, 0, {'\0'}}; 0, 0, 0, 0, 0, 0, {'\0'}};
const gfx::AcceleratedWidget kDefaultWidgetHandle = 1; const gfx::AcceleratedWidget kDefaultWidgetHandle = 1;
......
...@@ -12,17 +12,13 @@ ...@@ -12,17 +12,13 @@
#include "base/logging.h" #include "base/logging.h"
#include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_device.h"
#include "ui/ozone/platform/drm/gpu/drm_framebuffer.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/drm_gpu_util.h"
#include "ui/ozone/platform/drm/gpu/hardware_display_plane.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h"
namespace ui { namespace ui {
namespace {
constexpr float kFixedPointScaleValue = 1 << 16;
} // namespace
HardwareDisplayPlaneList::HardwareDisplayPlaneList() { HardwareDisplayPlaneList::HardwareDisplayPlaneList() {
atomic_property_set.reset(drmModeAtomicAlloc()); atomic_property_set.reset(drmModeAtomicAlloc());
...@@ -184,18 +180,14 @@ bool HardwareDisplayPlaneManager::AssignOverlayPlanes( ...@@ -184,18 +180,14 @@ bool HardwareDisplayPlaneManager::AssignOverlayPlanes(
gfx::Rect fixed_point_rect; gfx::Rect fixed_point_rect;
if (hw_plane->type() != HardwareDisplayPlane::kDummy) { if (hw_plane->type() != HardwareDisplayPlane::kDummy) {
const gfx::Size& size = plane.buffer->size(); const gfx::Size& size = plane.buffer->size();
gfx::RectF crop_rect = plane.crop_rect; gfx::RectF crop_rectf = plane.crop_rect;
crop_rect.Scale(size.width(), size.height()); crop_rectf.Scale(size.width(), size.height());
// DrmOverlayManager::CanHandleCandidate guarantees this is safe.
// This returns a number in 16.16 fixed point, required by the DRM overlay gfx::Rect crop_rect = gfx::ToNearestRect(crop_rectf);
// APIs. // Convert to 16.16 fixed point required by the DRM overlay APIs.
auto to_fixed_point = [](double v) -> uint32_t { fixed_point_rect =
return v * kFixedPointScaleValue; gfx::Rect(crop_rect.x() << 16, crop_rect.y() << 16,
}; crop_rect.width() << 16, crop_rect.height() << 16);
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)) { if (!SetPlaneData(plane_list, hw_plane, plane, crtc_id, fixed_point_rect)) {
......
...@@ -1141,6 +1141,72 @@ TEST_F(HardwareDisplayPlaneManagerPlanesReadyTest, ...@@ -1141,6 +1141,72 @@ TEST_F(HardwareDisplayPlaneManagerPlanesReadyTest,
EXPECT_TRUE(callback_called); EXPECT_TRUE(callback_called);
} }
TEST_P(HardwareDisplayPlaneManagerAtomicTest, OverlaySourceCrop) {
InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1);
fake_drm_->InitializeState(crtc_properties_, connector_properties_,
plane_properties_, property_names_, use_atomic_);
{
ui::DrmOverlayPlaneList assigns;
assigns.push_back(ui::DrmOverlayPlane(fake_buffer_, 0));
fake_drm_->plane_manager()->BeginFrame(&state_);
EXPECT_TRUE(fake_drm_->plane_manager()->AssignOverlayPlanes(
&state_, assigns, crtc_properties_[0].id));
std::unique_ptr<gfx::GpuFence> out_fence;
scoped_refptr<ui::PageFlipRequest> page_flip_request =
base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta());
EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&state_, page_flip_request,
&out_fence));
EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_W"));
EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_H"));
}
{
ui::DrmOverlayPlaneList assigns;
assigns.push_back(ui::DrmOverlayPlane(
fake_buffer_, 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE,
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, crtc_properties_[0].id));
scoped_refptr<ui::PageFlipRequest> page_flip_request =
base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta());
std::unique_ptr<gfx::GpuFence> out_fence;
EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&state_, page_flip_request,
&out_fence));
EXPECT_EQ(1u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_W"));
EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_H"));
}
{
ui::DrmOverlayPlaneList assigns;
assigns.push_back(ui::DrmOverlayPlane(
fake_buffer_, 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE,
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, crtc_properties_[0].id));
scoped_refptr<ui::PageFlipRequest> page_flip_request =
base::MakeRefCounted<ui::PageFlipRequest>(base::TimeDelta());
std::unique_ptr<gfx::GpuFence> out_fence;
EXPECT_TRUE(fake_drm_->plane_manager()->Commit(&state_, page_flip_request,
&out_fence));
EXPECT_EQ(2u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_W"));
EXPECT_EQ(1u << 16, GetPlanePropertyValue(kPlaneOffset, "SRC_H"));
}
}
class HardwareDisplayPlaneAtomicMock : public ui::HardwareDisplayPlaneAtomic { class HardwareDisplayPlaneAtomicMock : public ui::HardwareDisplayPlaneAtomic {
public: public:
HardwareDisplayPlaneAtomicMock() : ui::HardwareDisplayPlaneAtomic(1) {} HardwareDisplayPlaneAtomicMock() : ui::HardwareDisplayPlaneAtomic(1) {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment