blob: bf0bee4f5d71649ba96f5a5c277b575502029a85 [file] [log] [blame]
// Copyright 2016 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 "services/ui/ws/frame_generator.h"
#include <utility>
#include <vector>
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/render_pass.h"
#include "components/viz/common/quads/render_pass_draw_quad.h"
#include "components/viz/common/quads/shared_quad_state.h"
#include "components/viz/common/quads/surface_draw_quad.h"
#include "ui/gfx/geometry/size_f.h"
namespace ui {
namespace ws {
FrameGenerator::FrameGenerator() = default;
FrameGenerator::~FrameGenerator() = default;
void FrameGenerator::SetDeviceScaleFactor(float device_scale_factor) {
if (device_scale_factor_ == device_scale_factor)
return;
device_scale_factor_ = device_scale_factor;
SetNeedsBeginFrame(true);
}
void FrameGenerator::SetHighContrastMode(bool enabled) {
if (high_contrast_mode_enabled_ == enabled)
return;
high_contrast_mode_enabled_ = enabled;
SetNeedsBeginFrame(true);
}
void FrameGenerator::SetEmbeddedSurface(const viz::SurfaceInfo& surface_info) {
DCHECK(surface_info.is_valid());
// Only handle embedded surfaces changing here. The display root surface
// changing is handled immediately after the CompositorFrame is submitted.
if (surface_info != window_manager_surface_info_) {
window_manager_surface_info_ = surface_info;
SetNeedsBeginFrame(true);
}
}
void FrameGenerator::SwapSurfaceWith(FrameGenerator* other) {
viz::SurfaceInfo window_manager_surface_info = window_manager_surface_info_;
window_manager_surface_info_ = other->window_manager_surface_info_;
other->window_manager_surface_info_ = window_manager_surface_info;
if (window_manager_surface_info_.is_valid())
SetNeedsBeginFrame(true);
if (other->window_manager_surface_info_.is_valid())
other->SetNeedsBeginFrame(true);
}
void FrameGenerator::OnWindowDamaged() {
SetNeedsBeginFrame(true);
}
void FrameGenerator::OnWindowSizeChanged(const gfx::Size& pixel_size) {
if (pixel_size_ == pixel_size)
return;
pixel_size_ = pixel_size;
SetNeedsBeginFrame(true);
display_private_->Resize(pixel_size);
}
void FrameGenerator::Bind(
std::unique_ptr<viz::mojom::CompositorFrameSink> compositor_frame_sink,
viz::mojom::DisplayPrivateAssociatedPtr display_private) {
DCHECK(!compositor_frame_sink_);
compositor_frame_sink_ = std::move(compositor_frame_sink);
display_private_ = std::move(display_private);
}
void FrameGenerator::ReclaimResources(
const std::vector<viz::ReturnedResource>& resources) {
// Nothing to do here because FrameGenerator CompositorFrames don't reference
// any resources.
DCHECK(resources.empty());
}
void FrameGenerator::DidReceiveCompositorFrameAck(
const std::vector<viz::ReturnedResource>& resources) {}
void FrameGenerator::DidPresentCompositorFrame(
uint32_t presentation_token,
const gfx::PresentationFeedback& feedback) {
NOTIMPLEMENTED();
}
void FrameGenerator::OnBeginFrame(const viz::BeginFrameArgs& begin_frame_args) {
DCHECK(compositor_frame_sink_);
current_begin_frame_ack_ = viz::BeginFrameAck(begin_frame_args, false);
if (begin_frame_args.type == viz::BeginFrameArgs::MISSED) {
compositor_frame_sink_->DidNotProduceFrame(current_begin_frame_ack_);
return;
}
current_begin_frame_ack_.has_damage = true;
last_begin_frame_args_ = begin_frame_args;
// TODO(fsamuel): We should add a trace for generating a top level frame.
viz::CompositorFrame frame(GenerateCompositorFrame());
if (!id_allocator_.GetCurrentLocalSurfaceId().is_valid() ||
frame.size_in_pixels() != last_submitted_frame_size_ ||
frame.device_scale_factor() != last_device_scale_factor_) {
last_device_scale_factor_ = frame.device_scale_factor();
last_submitted_frame_size_ = frame.size_in_pixels();
id_allocator_.GenerateId();
}
compositor_frame_sink_->SubmitCompositorFrame(
id_allocator_.GetCurrentLocalSurfaceId(), std::move(frame),
GenerateHitTestRegionList(), 0);
SetNeedsBeginFrame(false);
}
viz::CompositorFrame FrameGenerator::GenerateCompositorFrame() {
const int render_pass_id = 1;
const gfx::Rect bounds(pixel_size_);
std::unique_ptr<viz::RenderPass> render_pass = viz::RenderPass::Create();
render_pass->SetNew(render_pass_id, bounds, bounds, gfx::Transform());
DrawWindow(render_pass.get());
viz::CompositorFrame frame;
frame.render_pass_list.push_back(std::move(render_pass));
if (high_contrast_mode_enabled_) {
std::unique_ptr<viz::RenderPass> invert_pass = viz::RenderPass::Create();
invert_pass->SetNew(2, bounds, bounds, gfx::Transform());
viz::SharedQuadState* shared_state =
invert_pass->CreateAndAppendSharedQuadState();
gfx::Size scaled_bounds = gfx::ScaleToCeiledSize(
pixel_size_, window_manager_surface_info_.device_scale_factor(),
window_manager_surface_info_.device_scale_factor());
bool is_clipped = false;
bool are_contents_opaque = false;
shared_state->SetAll(gfx::Transform(), gfx::Rect(scaled_bounds), bounds,
bounds, is_clipped, are_contents_opaque, 1.f,
SkBlendMode::kSrcOver, 0);
auto* quad =
invert_pass->CreateAndAppendDrawQuad<viz::RenderPassDrawQuad>();
frame.render_pass_list.back()->filters.Append(
cc::FilterOperation::CreateInvertFilter(1.f));
quad->SetNew(
shared_state, bounds, bounds, render_pass_id, 0 /* mask_resource_id */,
gfx::RectF() /* mask_uv_rect */, gfx::Size() /* mask_texture_size */,
gfx::Vector2dF() /* filters_scale */,
gfx::PointF() /* filters_origin */, gfx::RectF() /* tex_coord_rect */,
false /* force_anti_aliasing_off */);
frame.render_pass_list.push_back(std::move(invert_pass));
}
frame.metadata.device_scale_factor = device_scale_factor_;
frame.metadata.begin_frame_ack = current_begin_frame_ack_;
if (window_manager_surface_info_.is_valid()) {
frame.metadata.referenced_surfaces.push_back(
viz::SurfaceRange(window_manager_surface_info_.id()));
}
return frame;
}
viz::HitTestRegionList FrameGenerator::GenerateHitTestRegionList() const {
viz::HitTestRegionList hit_test_region_list;
hit_test_region_list.flags = viz::HitTestRegionFlags::kHitTestMine;
hit_test_region_list.bounds.set_size(pixel_size_);
viz::HitTestRegion hit_test_region;
hit_test_region.frame_sink_id =
window_manager_surface_info_.id().frame_sink_id();
hit_test_region.flags = viz::HitTestRegionFlags::kHitTestChildSurface;
hit_test_region.rect = gfx::Rect(pixel_size_);
hit_test_region_list.regions.push_back(std::move(hit_test_region));
return hit_test_region_list;
}
void FrameGenerator::DrawWindow(viz::RenderPass* pass) {
DCHECK(window_manager_surface_info_.is_valid());
const gfx::Rect bounds_at_origin(
window_manager_surface_info_.size_in_pixels());
gfx::Transform quad_to_target_transform;
if (scale_and_center_) {
// Determine the scaling to fit the source within the target.
gfx::SizeF source(window_manager_surface_info_.size_in_pixels());
const gfx::SizeF target(pixel_size_);
const float scale = std::min(target.width() / source.width(),
target.height() / source.height());
// Apply the transform to center the source within the output.
source.Scale(scale);
DCHECK(source.width() <= target.width() ||
source.height() <= target.height());
if (source.width() < target.width()) {
quad_to_target_transform.Translate(
(target.width() - source.width()) / 2.0f, 0);
} else if (source.height() < target.height()) {
quad_to_target_transform.Translate(
0, (target.height() - source.height()) / 2.0f);
}
// Apply the scaling after the transform.
quad_to_target_transform.Scale(scale, scale);
}
viz::SharedQuadState* sqs = pass->CreateAndAppendSharedQuadState();
gfx::Size scaled_bounds = gfx::ScaleToCeiledSize(
bounds_at_origin.size(),
window_manager_surface_info_.device_scale_factor(),
window_manager_surface_info_.device_scale_factor());
// TODO(fsamuel): These clipping and visible rects are incorrect. They need
// to be populated from CompositorFrame structs.
sqs->SetAll(quad_to_target_transform,
gfx::Rect(scaled_bounds) /* layer_rect */,
bounds_at_origin /* visible_layer_bounds */,
bounds_at_origin /* clip_rect */, false /* is_clipped */,
false /* are_contents_opaque */, 1.0f /* opacity */,
SkBlendMode::kSrcOver, 0 /* sorting-context_id */);
auto* quad = pass->CreateAndAppendDrawQuad<viz::SurfaceDrawQuad>();
quad->SetAll(sqs, bounds_at_origin /* rect */,
bounds_at_origin /* visible_rect */, true /* needs_blending*/,
viz::SurfaceRange(window_manager_surface_info_.id()),
SK_ColorWHITE /* default_background_color */,
false /* stretch_content_to_fill_bounds */);
}
void FrameGenerator::SetNeedsBeginFrame(bool needs_begin_frame) {
needs_begin_frame &= window_manager_surface_info_.is_valid();
compositor_frame_sink_->SetNeedsBeginFrame(needs_begin_frame);
}
} // namespace ws
} // namespace ui