hwc: adjust protected content scaling for gscaler width constraints
Protected content cannot be sent through the GPU. Instead slightly
over-scale to meet the Gscaler's 32-pixel alignment constraint and crop
back to the requested display frame.
Also move the minimum-width check to prevent a potential divide-by-zero
in the padding calculations.
Bug: 7365351
Change-Id: Ia0807fe6c4a6c25364fe08ed1f2eb223f3cb0d4e
Signed-off-by: Greg Hackmann <ghackmann@google.com>
diff --git a/libhwc/hwc.cpp b/libhwc/hwc.cpp
index 6ebc70b..e25d692 100644
--- a/libhwc/hwc.cpp
+++ b/libhwc/hwc.cpp
@@ -15,6 +15,7 @@
*/
#include <errno.h>
#include <fcntl.h>
+#include <math.h>
#include <poll.h>
#include <pthread.h>
#include <stdio.h>
@@ -55,6 +56,7 @@
const size_t MAX_PIXELS = 2560 * 1600 * 2;
const size_t GSC_W_ALIGNMENT = 16;
const size_t GSC_H_ALIGNMENT = 16;
+const size_t GSC_DST_CROP_W_ALIGNMENT_RGB888 = 32;
const size_t GSC_DST_W_ALIGNMENT_RGB888 = 32;
const size_t GSC_DST_H_ALIGNMENT_RGB888 = 1;
const size_t FIMD_GSC_IDX = 0;
@@ -196,6 +198,22 @@
template<typename T> inline T max(T a, T b) { return (a > b) ? a : b; }
template<typename T> inline T min(T a, T b) { return (a < b) ? a : b; }
+template<typename T> void align_crop_and_center(T &w, T &h,
+ hwc_rect_t *crop, size_t alignment)
+{
+ double aspect = 1.0 * h / w;
+ T w_orig = w, h_orig = h;
+
+ w = ALIGN(w, alignment);
+ h = round(aspect * w);
+ if (crop) {
+ crop->left = (w - w_orig) / 2;
+ crop->top = (h - h_orig) / 2;
+ crop->right = crop->left + w_orig;
+ crop->bottom = crop->top + h_orig;
+ }
+}
+
static bool is_transformed(const hwc_layer_1_t &layer)
{
return layer.transform != 0;
@@ -329,15 +347,12 @@
(layer.displayFrame.right % pixel_alignment) == 0;
}
-static bool dst_crop_w_aligned(const hwc_layer_1_t &layer, int format)
+static bool dst_crop_w_aligned(int dest_w)
{
- int dest_w;
int dst_crop_w_alignement;
- dest_w = WIDTH(layer.displayFrame);
-
/* GSC's dst crop size should be aligned 128Bytes */
- dst_crop_w_alignement = 32;
+ dst_crop_w_alignement = GSC_DST_CROP_W_ALIGNMENT_RGB888;
return (dest_w % dst_crop_w_alignement) == 0;
}
@@ -363,11 +378,16 @@
dest_w = WIDTH(layer.displayFrame);
dest_h = HEIGHT(layer.displayFrame);
}
+
+ if (handle->flags & GRALLOC_USAGE_PROTECTED)
+ align_crop_and_center(dest_w, dest_h, NULL,
+ GSC_DST_CROP_W_ALIGNMENT_RGB888);
+
int max_downscale = local_path ? 4 : 16;
const int max_upscale = 8;
return exynos5_format_is_supported_by_gscaler(format) &&
- dst_crop_w_aligned(layer,format) &&
+ dst_crop_w_aligned(dest_w) &&
handle->stride <= max_w &&
handle->stride % GSC_W_ALIGNMENT == 0 &&
src_w <= dest_w * max_downscale &&
@@ -600,7 +620,7 @@
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
- fmt.fmt.pix_mp.width = cfg.w;
+ fmt.fmt.pix_mp.width = h->stride;
fmt.fmt.pix_mp.height = cfg.h;
fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_BGR32;
fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
@@ -730,6 +750,10 @@
return false;
}
+ if (exynos5_visible_width(layer, handle->format, pdev) < BURSTLEN_BYTES) {
+ ALOGV("\tlayer %u: visible area is too narrow", i);
+ return false;
+ }
if (exynos5_requires_gscaler(layer, handle->format)) {
if (!exynos5_supports_gscaler(layer, handle->format, false)) {
ALOGV("\tlayer %u: gscaler required but not supported", i);
@@ -749,10 +773,6 @@
ALOGW("\tlayer %u: off-screen", i);
return false;
}
- if (exynos5_visible_width(layer, handle->format, pdev) < BURSTLEN_BYTES) {
- ALOGV("\tlayer %u: visible area is too narrow", i);
- return false;
- }
return true;
}
@@ -1041,7 +1061,7 @@
static int exynos5_config_gsc_m2m(hwc_layer_1_t &layer,
alloc_device_t* alloc_device, exynos5_gsc_data_t *gsc_data,
- int gsc_idx, int dst_format)
+ int gsc_idx, int dst_format, hwc_rect_t *sourceCrop)
{
ALOGV("configuring gscaler %u for memory-to-memory", AVAILABLE_GSC_UNITS[gsc_idx]);
@@ -1054,6 +1074,10 @@
memset(&src_cfg, 0, sizeof(src_cfg));
memset(&dst_cfg, 0, sizeof(dst_cfg));
+ hwc_rect_t sourceCropTemp;
+ if (!sourceCrop)
+ sourceCrop = &sourceCropTemp;
+
src_cfg.x = layer.sourceCrop.left;
src_cfg.y = layer.sourceCrop.top;
src_cfg.w = WIDTH(layer.sourceCrop);
@@ -1081,6 +1105,9 @@
dst_cfg.drmMode = src_cfg.drmMode;
dst_cfg.format = dst_format;
dst_cfg.narrowRgb = !exynos5_format_is_rgb(src_handle->format);
+ if (dst_cfg.drmMode)
+ align_crop_and_center(dst_cfg.w, dst_cfg.h, sourceCrop,
+ GSC_DST_CROP_W_ALIGNMENT_RGB888);
ALOGV("source configuration:");
dump_gsc_img(src_cfg);
@@ -1096,8 +1123,8 @@
if (src_handle->flags & GRALLOC_USAGE_PROTECTED)
usage |= GRALLOC_USAGE_PROTECTED;
- int w = ALIGN(WIDTH(layer.displayFrame), GSC_DST_W_ALIGNMENT_RGB888);
- int h = ALIGN(HEIGHT(layer.displayFrame), GSC_DST_H_ALIGNMENT_RGB888);
+ int w = ALIGN(dst_cfg.w, GSC_DST_W_ALIGNMENT_RGB888);
+ int h = ALIGN(dst_cfg.h, GSC_DST_H_ALIGNMENT_RGB888);
for (size_t i = 0; i < NUM_GSC_DST_BUFS; i++) {
if (gsc_data->dst_buf[i]) {
@@ -1135,6 +1162,12 @@
ALOGV("destination configuration:");
dump_gsc_img(dst_cfg);
+ if ((int)dst_cfg.w != WIDTH(layer.displayFrame))
+ ALOGV("padding %u x %u output to %u x %u and cropping to {%u,%u,%u,%u}",
+ WIDTH(layer.displayFrame), HEIGHT(layer.displayFrame),
+ dst_cfg.w, dst_cfg.h, sourceCrop->left, sourceCrop->top,
+ sourceCrop->right, sourceCrop->bottom);
+
if (gsc_data->gsc) {
ALOGV("reusing open gscaler %u", AVAILABLE_GSC_UNITS[gsc_idx]);
} else {
@@ -1328,8 +1361,10 @@
handle->format != HAL_PIXEL_FORMAT_RGB_565)
dst_format = HAL_PIXEL_FORMAT_RGBX_8888;
+ hwc_rect_t sourceCrop = { 0, 0,
+ WIDTH(layer.displayFrame), HEIGHT(layer.displayFrame) };
int err = exynos5_config_gsc_m2m(layer, pdev->alloc_device, &gsc,
- gsc_idx, dst_format);
+ gsc_idx, dst_format, &sourceCrop);
if (err < 0) {
ALOGE("failed to configure gscaler %u for layer %u",
gsc_idx, i);
@@ -1340,8 +1375,6 @@
buffer_handle_t dst_buf = gsc.dst_buf[gsc.current_buf];
private_handle_t *dst_handle =
private_handle_t::dynamicCast(dst_buf);
- hwc_rect_t sourceCrop = { 0, 0,
- WIDTH(layer.displayFrame), HEIGHT(layer.displayFrame) };
int fence = gsc.dst_cfg.releaseFenceFd;
exynos5_config_handle(dst_handle, sourceCrop,
layer.displayFrame, layer.blending, fence, config[i],
@@ -1490,7 +1523,7 @@
exynos5_gsc_data_t &gsc = pdev->gsc[HDMI_GSC_IDX];
int ret = exynos5_config_gsc_m2m(layer, pdev->alloc_device, &gsc, 1,
- HAL_PIXEL_FORMAT_RGBX_8888);
+ HAL_PIXEL_FORMAT_RGBX_8888, NULL);
if (ret < 0) {
ALOGE("failed to configure gscaler for video layer");
continue;