blob: 28b68a39f0a3cf8184c35c0337ec38d1c4b95991 [file] [log] [blame]
// Copyright 2017 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 "cc/output/dc_layer_overlay.h"
#include "base/metrics/histogram_macros.h"
#include "cc/base/math_util.h"
#include "cc/quads/solid_color_draw_quad.h"
#include "cc/quads/yuv_video_draw_quad.h"
#include "cc/resources/resource_provider.h"
#include "gpu/GLES2/gl2extchromium.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gl/gl_switches.h"
namespace cc {
namespace {
DCLayerOverlayProcessor::DCLayerResult FromYUVQuad(
ResourceProvider* resource_provider,
const YUVVideoDrawQuad* quad,
DCLayerOverlay* ca_layer_overlay) {
for (const auto& resource : quad->resources) {
if (!resource_provider->IsOverlayCandidate(resource))
return DCLayerOverlayProcessor::DC_LAYER_FAILED_TEXTURE_NOT_CANDIDATE;
}
ca_layer_overlay->resources = quad->resources;
ca_layer_overlay->contents_rect = quad->ya_tex_coord_rect;
ca_layer_overlay->filter = GL_LINEAR;
return DCLayerOverlayProcessor::DC_LAYER_SUCCESS;
}
// This returns the smallest rectangle in target space that contains the quad.
gfx::RectF ClippedQuadRectangle(const DrawQuad* quad) {
gfx::RectF quad_rect = MathUtil::MapClippedRect(
quad->shared_quad_state->quad_to_target_transform,
gfx::RectF(quad->rect));
if (quad->shared_quad_state->is_clipped)
quad_rect.Intersect(gfx::RectF(quad->shared_quad_state->clip_rect));
return quad_rect;
}
// Find a rectangle containing all the quads in a list that occlude the area
// in target_quad.
gfx::RectF GetOcclusionBounds(const gfx::RectF& target_quad,
QuadList::ConstIterator quad_list_begin,
QuadList::ConstIterator quad_list_end) {
gfx::RectF occlusion_bounding_box;
for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end;
++overlap_iter) {
float opacity = overlap_iter->shared_quad_state->opacity;
if (opacity < std::numeric_limits<float>::epsilon())
continue;
const DrawQuad* quad = *overlap_iter;
gfx::RectF overlap_rect = ClippedQuadRectangle(quad);
if (quad->material == DrawQuad::SOLID_COLOR) {
SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color;
float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity;
if (quad->ShouldDrawWithBlending() &&
alpha < std::numeric_limits<float>::epsilon())
continue;
}
overlap_rect.Intersect(target_quad);
if (!overlap_rect.IsEmpty()) {
occlusion_bounding_box.Union(overlap_rect);
}
}
return occlusion_bounding_box;
}
void RecordDCLayerResult(DCLayerOverlayProcessor::DCLayerResult result) {
UMA_HISTOGRAM_ENUMERATION("GPU.DirectComposition.DCLayerResult", result,
DCLayerOverlayProcessor::DC_LAYER_FAILED_MAX);
}
} // namespace
DCLayerOverlay::DCLayerOverlay() : filter(GL_LINEAR) {}
DCLayerOverlay::DCLayerOverlay(const DCLayerOverlay& other) = default;
DCLayerOverlay::~DCLayerOverlay() {}
DCLayerOverlayProcessor::DCLayerResult DCLayerOverlayProcessor::FromDrawQuad(
ResourceProvider* resource_provider,
const gfx::RectF& display_rect,
QuadList::ConstIterator quad_list_begin,
QuadList::ConstIterator quad,
DCLayerOverlay* ca_layer_overlay) {
if (quad->shared_quad_state->blend_mode != SkBlendMode::kSrcOver)
return DC_LAYER_FAILED_QUAD_BLEND_MODE;
DCLayerResult result;
switch (quad->material) {
case DrawQuad::YUV_VIDEO_CONTENT:
result =
FromYUVQuad(resource_provider, YUVVideoDrawQuad::MaterialCast(*quad),
ca_layer_overlay);
break;
default:
return DC_LAYER_FAILED_UNSUPPORTED_QUAD;
}
if (result != DC_LAYER_SUCCESS)
return result;
scoped_refptr<DCLayerOverlaySharedState> overlay_shared_state(
new DCLayerOverlaySharedState);
overlay_shared_state->z_order = 1;
overlay_shared_state->is_clipped = quad->shared_quad_state->is_clipped;
overlay_shared_state->clip_rect =
gfx::RectF(quad->shared_quad_state->clip_rect);
overlay_shared_state->opacity = quad->shared_quad_state->opacity;
overlay_shared_state->transform =
quad->shared_quad_state->quad_to_target_transform.matrix();
ca_layer_overlay->shared_state = overlay_shared_state;
ca_layer_overlay->bounds_rect = gfx::RectF(quad->rect);
return result;
}
void DCLayerOverlayProcessor::Process(ResourceProvider* resource_provider,
const gfx::RectF& display_rect,
QuadList* quad_list,
gfx::Rect* overlay_damage_rect,
gfx::Rect* damage_rect,
DCLayerOverlayList* ca_layer_overlays) {
gfx::Rect this_frame_underlay_rect;
for (auto it = quad_list->begin(); it != quad_list->end(); ++it) {
DCLayerOverlay dc_layer;
DCLayerResult result = FromDrawQuad(resource_provider, display_rect,
quad_list->begin(), it, &dc_layer);
if (result != DC_LAYER_SUCCESS) {
RecordDCLayerResult(result);
continue;
}
if (!it->shared_quad_state->quad_to_target_transform
.Preserves2dAxisAlignment() &&
!base::FeatureList::IsEnabled(
features::kDirectCompositionComplexOverlays)) {
RecordDCLayerResult(DC_LAYER_FAILED_COMPLEX_TRANSFORM);
continue;
}
gfx::Rect quad_rectangle = gfx::ToEnclosingRect(ClippedQuadRectangle(*it));
gfx::RectF occlusion_bounding_box =
GetOcclusionBounds(gfx::RectF(quad_rectangle), quad_list->begin(), it);
// Underlays are less efficient, so attempt regular overlays first.
if (ProcessForOverlay(display_rect, quad_list, quad_rectangle,
occlusion_bounding_box, it, damage_rect) ||
ProcessForUnderlay(display_rect, quad_list, quad_rectangle,
occlusion_bounding_box, it, damage_rect,
&this_frame_underlay_rect, &dc_layer)) {
overlay_damage_rect->Union(quad_rectangle);
RecordDCLayerResult(DC_LAYER_SUCCESS);
ca_layer_overlays->push_back(dc_layer);
// Only allow one overlay for now.
break;
}
}
damage_rect->Intersect(gfx::ToEnclosingRect(display_rect));
previous_frame_underlay_rect_ = this_frame_underlay_rect;
previous_display_rect_ = display_rect;
}
bool DCLayerOverlayProcessor::ProcessForOverlay(
const gfx::RectF& display_rect,
QuadList* quad_list,
const gfx::Rect& quad_rectangle,
const gfx::RectF& occlusion_bounding_box,
const QuadList::Iterator& it,
gfx::Rect* damage_rect) {
bool display_rect_changed = (display_rect != previous_display_rect_);
if (!occlusion_bounding_box.IsEmpty())
return false;
// The quad is on top, so promote it to an overlay and remove all damage
// underneath it.
if (it->shared_quad_state->quad_to_target_transform
.Preserves2dAxisAlignment() &&
!display_rect_changed && !it->ShouldDrawWithBlending()) {
damage_rect->Subtract(quad_rectangle);
}
quad_list->EraseAndInvalidateAllPointers(it);
return true;
}
bool DCLayerOverlayProcessor::ProcessForUnderlay(
const gfx::RectF& display_rect,
QuadList* quad_list,
const gfx::Rect& quad_rectangle,
const gfx::RectF& occlusion_bounding_box,
const QuadList::Iterator& it,
gfx::Rect* damage_rect,
gfx::Rect* this_frame_underlay_rect,
DCLayerOverlay* dc_layer) {
if (!base::FeatureList::IsEnabled(features::kDirectCompositionUnderlays)) {
RecordDCLayerResult(DC_LAYER_FAILED_OCCLUDED);
return false;
}
bool display_rect_changed = (display_rect != previous_display_rect_);
// The quad is occluded, so replace it with a black solid color quad and
// place the overlay itself under the quad.
if (it->shared_quad_state->quad_to_target_transform
.IsIdentityOrIntegerTranslation()) {
*this_frame_underlay_rect = quad_rectangle;
}
dc_layer->shared_state->z_order = -1;
const SharedQuadState* shared_quad_state = it->shared_quad_state;
gfx::Rect rect = it->visible_rect;
SolidColorDrawQuad* replacement =
quad_list->ReplaceExistingElement<SolidColorDrawQuad>(it);
replacement->SetAll(shared_quad_state, rect, rect, rect, false,
SK_ColorTRANSPARENT, true);
if (*this_frame_underlay_rect == previous_frame_underlay_rect_) {
// If this underlay rect is the same as for last frame, subtract its
// area from the damage of the main surface, as the cleared area was
// already cleared last frame. Add back the damage from the occluded
// area for this and last frame, as that may have changed.
if (it->shared_quad_state->quad_to_target_transform
.Preserves2dAxisAlignment() &&
!display_rect_changed) {
gfx::Rect occluding_damage_rect = *damage_rect;
occluding_damage_rect.Intersect(quad_rectangle);
damage_rect->Subtract(quad_rectangle);
gfx::Rect new_occlusion_bounding_box =
gfx::ToEnclosingRect(occlusion_bounding_box);
new_occlusion_bounding_box.Union(previous_occlusion_bounding_box_);
occluding_damage_rect.Intersect(new_occlusion_bounding_box);
damage_rect->Union(occluding_damage_rect);
}
} else {
// Entire replacement quad must be redrawn.
damage_rect->Union(quad_rectangle);
}
previous_occlusion_bounding_box_ =
gfx::ToEnclosingRect(occlusion_bounding_box);
return true;
}
} // namespace cc