blob: b6edb5b7558d02bda6e326477945e107ffb6843d [file] [log] [blame]
// Copyright 2015 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 "components/exo/surface.h"
#include <utility>
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_argument.h"
#include "cc/output/compositor_frame_sink.h"
#include "cc/quads/render_pass.h"
#include "cc/quads/shared_quad_state.h"
#include "cc/quads/solid_color_draw_quad.h"
#include "cc/quads/texture_draw_quad.h"
#include "cc/resources/single_release_callback.h"
#include "cc/surfaces/sequence_surface_reference_factory.h"
#include "cc/surfaces/surface.h"
#include "cc/surfaces/surface_manager.h"
#include "components/exo/buffer.h"
#include "components/exo/pointer.h"
#include "components/exo/surface_delegate.h"
#include "components/exo/surface_observer.h"
#include "third_party/khronos/GLES2/gl2.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_targeter.h"
#include "ui/base/class_property.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/hit_test.h"
#include "ui/compositor/layer.h"
#include "ui/events/event.h"
#include "ui/gfx/buffer_format_util.h"
#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/geometry/size_conversions.h"
#include "ui/gfx/gpu_memory_buffer.h"
#include "ui/gfx/path.h"
#include "ui/gfx/skia_util.h"
#include "ui/gfx/transform_util.h"
#include "ui/views/widget/widget.h"
DECLARE_UI_CLASS_PROPERTY_TYPE(exo::Surface*);
namespace exo {
namespace {
// A property key containing the surface that is associated with
// window. If unset, no surface is associated with window.
DEFINE_UI_CLASS_PROPERTY_KEY(Surface*, kSurfaceKey, nullptr);
// A property key to store whether the surface should only consume
// stylus input events.
DEFINE_UI_CLASS_PROPERTY_KEY(bool, kStylusOnlyKey, false);
// Helper function that returns an iterator to the first entry in |list|
// with |key|.
template <typename T, typename U>
typename T::iterator FindListEntry(T& list, U key) {
return std::find_if(list.begin(), list.end(),
[key](const typename T::value_type& entry) {
return entry.first == key;
});
}
// Helper function that returns true if |list| contains an entry with |key|.
template <typename T, typename U>
bool ListContainsEntry(T& list, U key) {
return FindListEntry(list, key) != list.end();
}
// Helper function that returns true if |format| may have an alpha channel.
// Note: False positives are allowed but false negatives are not.
bool FormatHasAlpha(gfx::BufferFormat format) {
switch (format) {
case gfx::BufferFormat::BGR_565:
case gfx::BufferFormat::RGBX_8888:
case gfx::BufferFormat::BGRX_8888:
case gfx::BufferFormat::YVU_420:
case gfx::BufferFormat::YUV_420_BIPLANAR:
case gfx::BufferFormat::UYVY_422:
return false;
default:
return true;
}
}
class CustomWindowDelegate : public aura::WindowDelegate {
public:
explicit CustomWindowDelegate(Surface* surface) : surface_(surface) {}
~CustomWindowDelegate() override {}
// Overridden from aura::WindowDelegate:
gfx::Size GetMinimumSize() const override { return gfx::Size(); }
gfx::Size GetMaximumSize() const override { return gfx::Size(); }
void OnBoundsChanged(const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) override {}
gfx::NativeCursor GetCursor(const gfx::Point& point) override {
return surface_->GetCursor();
}
int GetNonClientComponent(const gfx::Point& point) const override {
return HTNOWHERE;
}
bool ShouldDescendIntoChildForEventHandling(
aura::Window* child,
const gfx::Point& location) override {
return true;
}
bool CanFocus() override { return true; }
void OnCaptureLost() override {}
void OnPaint(const ui::PaintContext& context) override {}
void OnDeviceScaleFactorChanged(float device_scale_factor) override {
surface_->SetDeviceScaleFactor(device_scale_factor);
}
void OnWindowDestroying(aura::Window* window) override {}
void OnWindowDestroyed(aura::Window* window) override { delete this; }
void OnWindowTargetVisibilityChanged(bool visible) override {}
bool HasHitTestMask() const override { return surface_->HasHitTestMask(); }
void GetHitTestMask(gfx::Path* mask) const override {
surface_->GetHitTestMask(mask);
}
void OnKeyEvent(ui::KeyEvent* event) override {
// Propagates the key event upto the top-level views Widget so that we can
// trigger proper events in the views/ash level there. Event handling for
// Surfaces is done in a post event handler in keyboard.cc.
views::Widget* widget =
views::Widget::GetTopLevelWidgetForNativeView(surface_->window());
if (widget)
widget->OnKeyEvent(event);
}
private:
Surface* const surface_;
DISALLOW_COPY_AND_ASSIGN(CustomWindowDelegate);
};
class CustomWindowTargeter : public aura::WindowTargeter {
public:
CustomWindowTargeter() {}
~CustomWindowTargeter() override {}
// Overridden from aura::WindowTargeter:
bool SubtreeCanAcceptEvent(aura::Window* window,
const ui::LocatedEvent& event) const override {
Surface* surface = Surface::AsSurface(window);
if (!surface)
return false;
if (surface->IsStylusOnly()) {
ui::EventPointerType type = ui::EventPointerType::POINTER_TYPE_UNKNOWN;
if (event.IsTouchEvent()) {
auto* touch_event = static_cast<const ui::TouchEvent*>(&event);
type = touch_event->pointer_details().pointer_type;
}
if (type != ui::EventPointerType::POINTER_TYPE_PEN)
return false;
}
return aura::WindowTargeter::SubtreeCanAcceptEvent(window, event);
}
bool EventLocationInsideBounds(aura::Window* window,
const ui::LocatedEvent& event) const override {
Surface* surface = Surface::AsSurface(window);
if (!surface)
return false;
gfx::Point local_point = event.location();
if (window->parent())
aura::Window::ConvertPointToTarget(window->parent(), window,
&local_point);
return surface->HitTestRect(gfx::Rect(local_point, gfx::Size(1, 1)));
}
private:
DISALLOW_COPY_AND_ASSIGN(CustomWindowTargeter);
};
} // namespace
////////////////////////////////////////////////////////////////////////////////
// Surface, public:
Surface::Surface() : window_(new aura::Window(new CustomWindowDelegate(this))) {
window_->SetType(aura::client::WINDOW_TYPE_CONTROL);
window_->SetName("ExoSurface");
window_->SetProperty(kSurfaceKey, this);
window_->Init(ui::LAYER_SOLID_COLOR);
window_->SetEventTargeter(base::WrapUnique(new CustomWindowTargeter));
window_->set_owned_by_parent(false);
window_->AddObserver(this);
aura::Env::GetInstance()->context_factory()->AddObserver(this);
compositor_frame_sink_holder_ = base::MakeUnique<CompositorFrameSinkHolder>(
this, window_->CreateCompositorFrameSink());
}
Surface::~Surface() {
aura::Env::GetInstance()->context_factory()->RemoveObserver(this);
for (SurfaceObserver& observer : observers_)
observer.OnSurfaceDestroying(this);
window_->RemoveObserver(this);
if (window_->layer()->GetCompositor())
window_->layer()->GetCompositor()->vsync_manager()->RemoveObserver(this);
window_->layer()->SetShowSolidColorContent();
frame_callbacks_.splice(frame_callbacks_.end(), pending_frame_callbacks_);
active_frame_callbacks_.splice(active_frame_callbacks_.end(),
frame_callbacks_);
// Call all frame callbacks with a null frame time to indicate that they
// have been cancelled.
for (const auto& frame_callback : active_frame_callbacks_)
frame_callback.Run(base::TimeTicks());
presentation_callbacks_.splice(presentation_callbacks_.end(),
pending_presentation_callbacks_);
swapping_presentation_callbacks_.splice(
swapping_presentation_callbacks_.end(), presentation_callbacks_);
swapped_presentation_callbacks_.splice(swapped_presentation_callbacks_.end(),
swapping_presentation_callbacks_);
// Call all presentation callbacks with a null presentation time to indicate
// that they have been cancelled.
for (const auto& presentation_callback : swapped_presentation_callbacks_)
presentation_callback.Run(base::TimeTicks(), base::TimeDelta());
}
// static
Surface* Surface::AsSurface(const aura::Window* window) {
return window->GetProperty(kSurfaceKey);
}
cc::SurfaceId Surface::GetSurfaceId() const {
return window_->GetSurfaceId();
}
void Surface::Attach(Buffer* buffer) {
TRACE_EVENT1("exo", "Surface::Attach", "buffer",
buffer ? buffer->GetSize().ToString() : "null");
has_pending_contents_ = true;
pending_buffer_.Reset(buffer ? buffer->AsWeakPtr() : base::WeakPtr<Buffer>());
}
void Surface::Damage(const gfx::Rect& damage) {
TRACE_EVENT1("exo", "Surface::Damage", "damage", damage.ToString());
pending_damage_.op(gfx::RectToSkIRect(damage), SkRegion::kUnion_Op);
}
void Surface::RequestFrameCallback(const FrameCallback& callback) {
TRACE_EVENT0("exo", "Surface::RequestFrameCallback");
pending_frame_callbacks_.push_back(callback);
}
void Surface::RequestPresentationCallback(
const PresentationCallback& callback) {
TRACE_EVENT0("exo", "Surface::RequestPresentationCallback");
pending_presentation_callbacks_.push_back(callback);
}
void Surface::SetOpaqueRegion(const SkRegion& region) {
TRACE_EVENT1("exo", "Surface::SetOpaqueRegion", "region",
gfx::SkIRectToRect(region.getBounds()).ToString());
pending_state_.opaque_region = region;
}
void Surface::SetInputRegion(const SkRegion& region) {
TRACE_EVENT1("exo", "Surface::SetInputRegion", "region",
gfx::SkIRectToRect(region.getBounds()).ToString());
pending_state_.input_region = region;
}
void Surface::SetBufferScale(float scale) {
TRACE_EVENT1("exo", "Surface::SetBufferScale", "scale", scale);
pending_state_.buffer_scale = scale;
}
void Surface::AddSubSurface(Surface* sub_surface) {
TRACE_EVENT1("exo", "Surface::AddSubSurface", "sub_surface",
sub_surface->AsTracedValue());
DCHECK(!sub_surface->window()->parent());
DCHECK(!sub_surface->window()->IsVisible());
window_->AddChild(sub_surface->window());
DCHECK(!ListContainsEntry(pending_sub_surfaces_, sub_surface));
pending_sub_surfaces_.push_back(std::make_pair(sub_surface, gfx::Point()));
has_pending_layer_changes_ = true;
}
void Surface::RemoveSubSurface(Surface* sub_surface) {
TRACE_EVENT1("exo", "Surface::RemoveSubSurface", "sub_surface",
sub_surface->AsTracedValue());
window_->RemoveChild(sub_surface->window());
if (sub_surface->window()->IsVisible())
sub_surface->window()->Hide();
DCHECK(ListContainsEntry(pending_sub_surfaces_, sub_surface));
pending_sub_surfaces_.erase(
FindListEntry(pending_sub_surfaces_, sub_surface));
has_pending_layer_changes_ = true;
}
void Surface::SetSubSurfacePosition(Surface* sub_surface,
const gfx::Point& position) {
TRACE_EVENT2("exo", "Surface::SetSubSurfacePosition", "sub_surface",
sub_surface->AsTracedValue(), "position", position.ToString());
auto it = FindListEntry(pending_sub_surfaces_, sub_surface);
DCHECK(it != pending_sub_surfaces_.end());
if (it->second == position)
return;
it->second = position;
has_pending_layer_changes_ = true;
}
void Surface::PlaceSubSurfaceAbove(Surface* sub_surface, Surface* reference) {
TRACE_EVENT2("exo", "Surface::PlaceSubSurfaceAbove", "sub_surface",
sub_surface->AsTracedValue(), "reference",
reference->AsTracedValue());
if (sub_surface == reference) {
DLOG(WARNING) << "Client tried to place sub-surface above itself";
return;
}
auto position_it = pending_sub_surfaces_.begin();
if (reference != this) {
position_it = FindListEntry(pending_sub_surfaces_, reference);
if (position_it == pending_sub_surfaces_.end()) {
DLOG(WARNING) << "Client tried to place sub-surface above a reference "
"surface that is neither a parent nor a sibling";
return;
}
// Advance iterator to have |position_it| point to the sibling surface
// above |reference|.
++position_it;
}
DCHECK(ListContainsEntry(pending_sub_surfaces_, sub_surface));
auto it = FindListEntry(pending_sub_surfaces_, sub_surface);
if (it == position_it)
return;
pending_sub_surfaces_.splice(position_it, pending_sub_surfaces_, it);
has_pending_layer_changes_ = true;
}
void Surface::PlaceSubSurfaceBelow(Surface* sub_surface, Surface* sibling) {
TRACE_EVENT2("exo", "Surface::PlaceSubSurfaceBelow", "sub_surface",
sub_surface->AsTracedValue(), "sibling",
sibling->AsTracedValue());
if (sub_surface == sibling) {
DLOG(WARNING) << "Client tried to place sub-surface below itself";
return;
}
auto sibling_it = FindListEntry(pending_sub_surfaces_, sibling);
if (sibling_it == pending_sub_surfaces_.end()) {
DLOG(WARNING) << "Client tried to place sub-surface below a surface that "
"is not a sibling";
return;
}
DCHECK(ListContainsEntry(pending_sub_surfaces_, sub_surface));
auto it = FindListEntry(pending_sub_surfaces_, sub_surface);
if (it == sibling_it)
return;
pending_sub_surfaces_.splice(sibling_it, pending_sub_surfaces_, it);
has_pending_layer_changes_ = true;
}
void Surface::SetViewport(const gfx::Size& viewport) {
TRACE_EVENT1("exo", "Surface::SetViewport", "viewport", viewport.ToString());
pending_state_.viewport = viewport;
}
void Surface::SetCrop(const gfx::RectF& crop) {
TRACE_EVENT1("exo", "Surface::SetCrop", "crop", crop.ToString());
pending_state_.crop = crop;
}
void Surface::SetOnlyVisibleOnSecureOutput(bool only_visible_on_secure_output) {
TRACE_EVENT1("exo", "Surface::SetOnlyVisibleOnSecureOutput",
"only_visible_on_secure_output", only_visible_on_secure_output);
pending_state_.only_visible_on_secure_output = only_visible_on_secure_output;
}
void Surface::SetBlendMode(SkBlendMode blend_mode) {
TRACE_EVENT1("exo", "Surface::SetBlendMode", "blend_mode",
static_cast<int>(blend_mode));
pending_state_.blend_mode = blend_mode;
}
void Surface::SetAlpha(float alpha) {
TRACE_EVENT1("exo", "Surface::SetAlpha", "alpha", alpha);
pending_state_.alpha = alpha;
}
void Surface::SetDeviceScaleFactor(float device_scale_factor) {
device_scale_factor_ = device_scale_factor;
}
void Surface::Commit() {
TRACE_EVENT0("exo", "Surface::Commit");
needs_commit_surface_hierarchy_ = true;
if (state_ != pending_state_)
has_pending_layer_changes_ = true;
if (has_pending_contents_) {
if (pending_buffer_.buffer()) {
if (current_resource_.size != pending_buffer_.buffer()->GetSize())
has_pending_layer_changes_ = true;
// Whether layer fills bounds opaquely or not might have changed.
if (current_resource_has_alpha_ !=
FormatHasAlpha(pending_buffer_.buffer()->GetFormat()))
has_pending_layer_changes_ = true;
} else if (!current_resource_.size.IsEmpty()) {
has_pending_layer_changes_ = true;
}
}
if (delegate_) {
delegate_->OnSurfaceCommit();
} else {
CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces();
CommitSurfaceHierarchy();
}
if (current_begin_frame_ack_.sequence_number !=
cc::BeginFrameArgs::kInvalidFrameNumber) {
if (!current_begin_frame_ack_.has_damage) {
compositor_frame_sink_holder_->GetCompositorFrameSink()
->DidNotProduceFrame(current_begin_frame_ack_);
}
current_begin_frame_ack_.sequence_number =
cc::BeginFrameArgs::kInvalidFrameNumber;
if (begin_frame_source_)
begin_frame_source_->DidFinishFrame(this);
}
}
void Surface::CommitSurfaceHierarchy() {
DCHECK(needs_commit_surface_hierarchy_);
needs_commit_surface_hierarchy_ = false;
has_pending_layer_changes_ = false;
state_ = pending_state_;
pending_state_.only_visible_on_secure_output = false;
// We update contents if Attach() has been called since last commit.
if (has_pending_contents_) {
has_pending_contents_ = false;
current_buffer_ = std::move(pending_buffer_);
UpdateResource(true);
}
// Move pending frame callbacks to the end of frame_callbacks_.
frame_callbacks_.splice(frame_callbacks_.end(), pending_frame_callbacks_);
// Move pending presentation callbacks to the end of presentation_callbacks_.
presentation_callbacks_.splice(presentation_callbacks_.end(),
pending_presentation_callbacks_);
UpdateSurface(false);
if (needs_commit_to_new_surface_) {
needs_commit_to_new_surface_ = false;
window_->layer()->SetFillsBoundsOpaquely(
!current_resource_has_alpha_ ||
state_.blend_mode == SkBlendMode::kSrc ||
state_.opaque_region.contains(
gfx::RectToSkIRect(gfx::Rect(content_size_))));
}
// Reset damage.
pending_damage_.setEmpty();
DCHECK(!current_resource_.id ||
compositor_frame_sink_holder_->HasReleaseCallbackForResource(
current_resource_.id));
// Synchronize window hierarchy. This will position and update the stacking
// order of all sub-surfaces after committing all pending state of sub-surface
// descendants.
aura::Window* stacking_target = nullptr;
for (auto& sub_surface_entry : pending_sub_surfaces_) {
Surface* sub_surface = sub_surface_entry.first;
// Synchronsouly commit all pending state of the sub-surface and its
// decendents.
if (sub_surface->needs_commit_surface_hierarchy())
sub_surface->CommitSurfaceHierarchy();
// Enable/disable sub-surface based on if it has contents.
if (sub_surface->has_contents())
sub_surface->window()->Show();
else
sub_surface->window()->Hide();
// Move sub-surface to its new position in the stack.
if (stacking_target)
window_->StackChildAbove(sub_surface->window(), stacking_target);
// Stack next sub-surface above this sub-surface.
stacking_target = sub_surface->window();
// Update sub-surface position relative to surface origin.
sub_surface->window()->SetBounds(gfx::Rect(
sub_surface_entry.second, sub_surface->window()->layer()->size()));
}
}
bool Surface::IsSynchronized() const {
return delegate_ ? delegate_->IsSurfaceSynchronized() : false;
}
gfx::Rect Surface::GetHitTestBounds() const {
SkIRect bounds = state_.input_region.getBounds();
if (!bounds.intersect(
gfx::RectToSkIRect(gfx::Rect(window_->layer()->size()))))
return gfx::Rect();
return gfx::SkIRectToRect(bounds);
}
bool Surface::HitTestRect(const gfx::Rect& rect) const {
if (HasHitTestMask())
return state_.input_region.intersects(gfx::RectToSkIRect(rect));
return rect.Intersects(gfx::Rect(window_->layer()->size()));
}
bool Surface::HasHitTestMask() const {
return !state_.input_region.contains(
gfx::RectToSkIRect(gfx::Rect(window_->layer()->size())));
}
void Surface::GetHitTestMask(gfx::Path* mask) const {
state_.input_region.getBoundaryPath(mask);
}
void Surface::RegisterCursorProvider(Pointer* provider) {
cursor_providers_.insert(provider);
}
void Surface::UnregisterCursorProvider(Pointer* provider) {
cursor_providers_.erase(provider);
}
gfx::NativeCursor Surface::GetCursor() {
// What cursor we display when we have multiple cursor providers is not
// important. Return the first non-null cursor.
for (auto* cursor_provider : cursor_providers_) {
gfx::NativeCursor cursor = cursor_provider->GetCursor();
if (cursor != ui::CursorType::kNull)
return cursor;
}
return ui::CursorType::kNull;
}
void Surface::SetSurfaceDelegate(SurfaceDelegate* delegate) {
DCHECK(!delegate_ || !delegate);
delegate_ = delegate;
}
bool Surface::HasSurfaceDelegate() const {
return !!delegate_;
}
void Surface::AddSurfaceObserver(SurfaceObserver* observer) {
observers_.AddObserver(observer);
}
void Surface::RemoveSurfaceObserver(SurfaceObserver* observer) {
observers_.RemoveObserver(observer);
}
bool Surface::HasSurfaceObserver(const SurfaceObserver* observer) const {
return observers_.HasObserver(observer);
}
std::unique_ptr<base::trace_event::TracedValue> Surface::AsTracedValue() const {
std::unique_ptr<base::trace_event::TracedValue> value(
new base::trace_event::TracedValue());
value->SetString("name", window_->layer()->name());
return value;
}
void Surface::DidReceiveCompositorFrameAck() {
active_frame_callbacks_.splice(active_frame_callbacks_.end(),
frame_callbacks_);
swapping_presentation_callbacks_.splice(
swapping_presentation_callbacks_.end(), presentation_callbacks_);
UpdateNeedsBeginFrame();
}
void Surface::SetBeginFrameSource(cc::BeginFrameSource* begin_frame_source) {
if (needs_begin_frame_) {
DCHECK(begin_frame_source_);
begin_frame_source_->RemoveObserver(this);
needs_begin_frame_ = false;
}
begin_frame_source_ = begin_frame_source;
UpdateNeedsBeginFrame();
}
void Surface::UpdateNeedsBeginFrame() {
if (!begin_frame_source_)
return;
bool needs_begin_frame = !active_frame_callbacks_.empty();
if (needs_begin_frame == needs_begin_frame_)
return;
needs_begin_frame_ = needs_begin_frame;
if (needs_begin_frame_)
begin_frame_source_->AddObserver(this);
else
begin_frame_source_->RemoveObserver(this);
}
bool Surface::OnBeginFrameDerivedImpl(const cc::BeginFrameArgs& args) {
current_begin_frame_ack_ = cc::BeginFrameAck(
args.source_id, args.sequence_number, args.sequence_number, false);
while (!active_frame_callbacks_.empty()) {
active_frame_callbacks_.front().Run(args.frame_time);
active_frame_callbacks_.pop_front();
}
return true;
}
void Surface::CheckIfSurfaceHierarchyNeedsCommitToNewSurfaces() {
if (HasLayerHierarchyChanged())
SetSurfaceHierarchyNeedsCommitToNewSurfaces();
}
bool Surface::IsStylusOnly() {
return window_->GetProperty(kStylusOnlyKey);
}
void Surface::SetStylusOnly() {
window_->SetProperty(kStylusOnlyKey, true);
}
////////////////////////////////////////////////////////////////////////////////
// ui::ContextFactoryObserver overrides:
void Surface::OnLostResources() {
if (!window_->GetSurfaceId().is_valid())
return;
UpdateResource(false);
UpdateSurface(true);
}
////////////////////////////////////////////////////////////////////////////////
// aura::WindowObserver overrides:
void Surface::OnWindowAddedToRootWindow(aura::Window* window) {
window->layer()->GetCompositor()->vsync_manager()->AddObserver(this);
}
void Surface::OnWindowRemovingFromRootWindow(aura::Window* window,
aura::Window* new_root) {
window->layer()->GetCompositor()->vsync_manager()->RemoveObserver(this);
}
////////////////////////////////////////////////////////////////////////////////
// ui::CompositorVSyncManager::Observer overrides:
void Surface::OnUpdateVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) {
// Use current time if platform doesn't provide an accurate timebase.
if (timebase.is_null())
timebase = base::TimeTicks::Now();
while (!swapped_presentation_callbacks_.empty()) {
swapped_presentation_callbacks_.front().Run(timebase, interval);
swapped_presentation_callbacks_.pop_front();
}
// VSync parameters updates are generated at the start of a new swap. Move
// the swapping presentation callbacks to swapped callbacks so they fire
// at the next VSync parameters update as that will contain the presentation
// time for the previous frame.
swapped_presentation_callbacks_.splice(swapped_presentation_callbacks_.end(),
swapping_presentation_callbacks_);
}
////////////////////////////////////////////////////////////////////////////////
// Buffer, private:
Surface::State::State() : input_region(SkIRect::MakeLargest()) {}
Surface::State::~State() = default;
bool Surface::State::operator==(const State& other) {
return (other.crop == crop && alpha == other.alpha &&
other.blend_mode == blend_mode && other.viewport == viewport &&
other.opaque_region == opaque_region &&
other.buffer_scale == buffer_scale &&
other.input_region == input_region);
}
Surface::BufferAttachment::BufferAttachment() {}
Surface::BufferAttachment::~BufferAttachment() {
if (buffer_)
buffer_->OnDetach();
}
Surface::BufferAttachment& Surface::BufferAttachment::operator=(
BufferAttachment&& other) {
if (buffer_)
buffer_->OnDetach();
buffer_ = other.buffer_;
other.buffer_ = base::WeakPtr<Buffer>();
return *this;
}
base::WeakPtr<Buffer>& Surface::BufferAttachment::buffer() {
return buffer_;
}
const base::WeakPtr<Buffer>& Surface::BufferAttachment::buffer() const {
return buffer_;
}
void Surface::BufferAttachment::Reset(base::WeakPtr<Buffer> buffer) {
if (buffer)
buffer->OnAttach();
if (buffer_)
buffer_->OnDetach();
buffer_ = buffer;
}
bool Surface::HasLayerHierarchyChanged() const {
if (needs_commit_surface_hierarchy_ && has_pending_layer_changes_)
return true;
for (const auto& sub_surface_entry : pending_sub_surfaces_) {
if (sub_surface_entry.first->HasLayerHierarchyChanged())
return true;
}
return false;
}
void Surface::SetSurfaceHierarchyNeedsCommitToNewSurfaces() {
needs_commit_to_new_surface_ = true;
for (auto& sub_surface_entry : pending_sub_surfaces_) {
sub_surface_entry.first->SetSurfaceHierarchyNeedsCommitToNewSurfaces();
}
}
void Surface::UpdateResource(bool client_usage) {
if (current_buffer_.buffer() &&
current_buffer_.buffer()->ProduceTransferableResource(
compositor_frame_sink_holder_.get(), next_resource_id_++,
state_.only_visible_on_secure_output, client_usage,
&current_resource_)) {
current_resource_has_alpha_ =
FormatHasAlpha(current_buffer_.buffer()->GetFormat());
} else {
current_resource_.id = 0;
current_resource_.size = gfx::Size();
current_resource_has_alpha_ = false;
}
}
void Surface::UpdateSurface(bool full_damage) {
gfx::Size buffer_size = current_resource_.size;
gfx::SizeF scaled_buffer_size(
gfx::ScaleSize(gfx::SizeF(buffer_size), 1.0f / state_.buffer_scale));
gfx::Size layer_size; // Size of the output layer, in DIP.
if (!state_.viewport.IsEmpty()) {
layer_size = state_.viewport;
} else if (!state_.crop.IsEmpty()) {
DLOG_IF(WARNING, !gfx::IsExpressibleAsInt(state_.crop.width()) ||
!gfx::IsExpressibleAsInt(state_.crop.height()))
<< "Crop rectangle size (" << state_.crop.size().ToString()
<< ") most be expressible using integers when viewport is not set";
layer_size = gfx::ToCeiledSize(state_.crop.size());
} else {
layer_size = gfx::ToCeiledSize(scaled_buffer_size);
}
content_size_ = layer_size;
// We need update window_'s bounds with content size, because the
// CompositorFrameSink may not update the window's size base the size of
// the lastest submitted CompositorFrame.
window_->SetBounds(gfx::Rect(window_->bounds().origin(), content_size_));
// TODO(jbauman): Figure out how this interacts with the pixel size of
// CopyOutputRequests on the layer.
gfx::Size contents_surface_size = layer_size;
gfx::PointF uv_top_left(0.f, 0.f);
gfx::PointF uv_bottom_right(1.f, 1.f);
if (!state_.crop.IsEmpty()) {
uv_top_left = state_.crop.origin();
uv_top_left.Scale(1.f / scaled_buffer_size.width(),
1.f / scaled_buffer_size.height());
uv_bottom_right = state_.crop.bottom_right();
uv_bottom_right.Scale(1.f / scaled_buffer_size.width(),
1.f / scaled_buffer_size.height());
}
gfx::Rect damage_rect;
gfx::Rect output_rect = gfx::Rect(contents_surface_size);
if (full_damage) {
damage_rect = output_rect;
} else {
// pending_damage_ is in Surface coordinates.
damage_rect = gfx::SkIRectToRect(pending_damage_.getBounds());
damage_rect.Intersect(output_rect);
}
const int kRenderPassId = 1;
std::unique_ptr<cc::RenderPass> render_pass = cc::RenderPass::Create();
render_pass->SetNew(kRenderPassId, output_rect, damage_rect,
gfx::Transform());
gfx::Rect quad_rect = output_rect;
cc::SharedQuadState* quad_state =
render_pass->CreateAndAppendSharedQuadState();
quad_state->quad_layer_rect = gfx::Rect(contents_surface_size);
quad_state->visible_quad_layer_rect = quad_rect;
quad_state->opacity = state_.alpha;
cc::CompositorFrame frame;
// If we commit while we don't have an active BeginFrame, we acknowledge a
// manual one.
if (current_begin_frame_ack_.sequence_number ==
cc::BeginFrameArgs::kInvalidFrameNumber) {
current_begin_frame_ack_ = cc::BeginFrameAck::CreateManualAckWithDamage();
} else {
current_begin_frame_ack_.has_damage = true;
}
frame.metadata.begin_frame_ack = current_begin_frame_ack_;
frame.metadata.device_scale_factor = device_scale_factor_;
if (current_resource_.id) {
// Texture quad is only needed if buffer is not fully transparent.
if (state_.alpha) {
cc::TextureDrawQuad* texture_quad =
render_pass->CreateAndAppendDrawQuad<cc::TextureDrawQuad>();
float vertex_opacity[4] = {1.0, 1.0, 1.0, 1.0};
gfx::Rect opaque_rect;
if (!current_resource_has_alpha_ ||
state_.blend_mode == SkBlendMode::kSrc ||
state_.opaque_region.contains(gfx::RectToSkIRect(quad_rect))) {
opaque_rect = quad_rect;
} else if (state_.opaque_region.isRect()) {
opaque_rect = gfx::SkIRectToRect(state_.opaque_region.getBounds());
}
texture_quad->SetNew(quad_state, quad_rect, opaque_rect, quad_rect,
current_resource_.id, true, uv_top_left,
uv_bottom_right, SK_ColorTRANSPARENT, vertex_opacity,
false, false, state_.only_visible_on_secure_output);
if (current_resource_.is_overlay_candidate)
texture_quad->set_resource_size_in_pixels(current_resource_.size);
frame.resource_list.push_back(current_resource_);
}
} else {
cc::SolidColorDrawQuad* solid_quad =
render_pass->CreateAndAppendDrawQuad<cc::SolidColorDrawQuad>();
solid_quad->SetNew(quad_state, quad_rect, quad_rect, SK_ColorBLACK, false);
}
frame.render_pass_list.push_back(std::move(render_pass));
compositor_frame_sink_holder_->GetCompositorFrameSink()
->SubmitCompositorFrame(std::move(frame));
}
} // namespace exo