| // Copyright 2014 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 "android_webview/browser/hardware_renderer.h" |
| |
| #include <utility> |
| |
| #include "android_webview/browser/aw_gl_surface.h" |
| #include "android_webview/browser/aw_render_thread_context_provider.h" |
| #include "android_webview/browser/parent_compositor_draw_constraints.h" |
| #include "android_webview/browser/render_thread_manager.h" |
| #include "android_webview/browser/surfaces_instance.h" |
| #include "android_webview/public/browser/draw_gl.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/trace_event/trace_event.h" |
| #include "cc/output/compositor_frame.h" |
| #include "cc/surfaces/compositor_frame_sink_support.h" |
| #include "cc/surfaces/local_surface_id_allocator.h" |
| #include "cc/surfaces/surface_manager.h" |
| #include "ui/gfx/transform.h" |
| #include "ui/gl/gl_bindings.h" |
| |
| namespace android_webview { |
| |
| HardwareRenderer::HardwareRenderer(RenderThreadManager* state) |
| : render_thread_manager_(state), |
| last_egl_context_(eglGetCurrentContext()), |
| surfaces_(SurfacesInstance::GetOrCreateInstance()), |
| frame_sink_id_(surfaces_->AllocateFrameSinkId()), |
| local_surface_id_allocator_( |
| base::MakeUnique<cc::LocalSurfaceIdAllocator>()), |
| last_committed_compositor_frame_sink_id_(0u), |
| last_submitted_compositor_frame_sink_id_(0u) { |
| DCHECK(last_egl_context_); |
| surfaces_->GetSurfaceManager()->RegisterFrameSinkId(frame_sink_id_); |
| CreateNewCompositorFrameSinkSupport(); |
| } |
| |
| HardwareRenderer::~HardwareRenderer() { |
| // Must reset everything before |surface_factory_| to ensure all |
| // resources are returned before resetting. |
| if (child_id_.is_valid()) |
| DestroySurface(); |
| support_.reset(); |
| surfaces_->GetSurfaceManager()->InvalidateFrameSinkId(frame_sink_id_); |
| |
| // Reset draw constraints. |
| render_thread_manager_->PostExternalDrawConstraintsToChildCompositorOnRT( |
| ParentCompositorDrawConstraints()); |
| for (auto& child_frame : child_frame_queue_) { |
| child_frame->WaitOnFutureIfNeeded(); |
| ReturnChildFrame(std::move(child_frame)); |
| } |
| } |
| |
| void HardwareRenderer::CommitFrame() { |
| TRACE_EVENT0("android_webview", "CommitFrame"); |
| scroll_offset_ = render_thread_manager_->GetScrollOffsetOnRT(); |
| ChildFrameQueue child_frames = render_thread_manager_->PassFramesOnRT(); |
| // |child_frames| should have at most one non-empty frame, and one current |
| // and unwaited frame, in that order. |
| DCHECK_LE(child_frames.size(), 2u); |
| if (child_frames.empty()) |
| return; |
| // Insert all except last, ie current frame. |
| while (child_frames.size() > 1u) { |
| child_frame_queue_.emplace_back(std::move(child_frames.front())); |
| child_frames.pop_front(); |
| } |
| for (auto& pruned_frame : WaitAndPruneFrameQueue(&child_frame_queue_)) |
| ReturnChildFrame(std::move(pruned_frame)); |
| DCHECK_LE(child_frame_queue_.size(), 1u); |
| child_frame_queue_.emplace_back(std::move(child_frames.front())); |
| } |
| |
| void HardwareRenderer::DrawGL(AwDrawGLInfo* draw_info) { |
| TRACE_EVENT0("android_webview", "HardwareRenderer::DrawGL"); |
| |
| for (auto& pruned_frame : WaitAndPruneFrameQueue(&child_frame_queue_)) |
| ReturnChildFrame(std::move(pruned_frame)); |
| DCHECK_LE(child_frame_queue_.size(), 1u); |
| if (!child_frame_queue_.empty()) { |
| child_frame_ = std::move(child_frame_queue_.front()); |
| child_frame_queue_.clear(); |
| } |
| if (child_frame_) { |
| last_committed_compositor_frame_sink_id_ = |
| child_frame_->compositor_frame_sink_id; |
| } |
| |
| // We need to watch if the current Android context has changed and enforce |
| // a clean-up in the compositor. |
| EGLContext current_context = eglGetCurrentContext(); |
| DCHECK(current_context) << "DrawGL called without EGLContext"; |
| |
| // TODO(boliu): Handle context loss. |
| if (last_egl_context_ != current_context) |
| DLOG(WARNING) << "EGLContextChanged"; |
| |
| // SurfaceFactory::SubmitCompositorFrame might call glFlush. So calling it |
| // during "kModeSync" stage (which does not allow GL) might result in extra |
| // kModeProcess. Instead, submit the frame in "kModeDraw" stage to avoid |
| // unnecessary kModeProcess. |
| if (child_frame_.get() && child_frame_->frame.get()) { |
| if (!compositor_id_.Equals(child_frame_->compositor_id) || |
| last_submitted_compositor_frame_sink_id_ != |
| child_frame_->compositor_frame_sink_id) { |
| if (child_id_.is_valid()) |
| DestroySurface(); |
| |
| CreateNewCompositorFrameSinkSupport(); |
| compositor_id_ = child_frame_->compositor_id; |
| last_submitted_compositor_frame_sink_id_ = |
| child_frame_->compositor_frame_sink_id; |
| } |
| |
| std::unique_ptr<cc::CompositorFrame> child_compositor_frame = |
| std::move(child_frame_->frame); |
| |
| gfx::Size frame_size = |
| child_compositor_frame->render_pass_list.back()->output_rect.size(); |
| bool size_changed = frame_size != frame_size_; |
| frame_size_ = frame_size; |
| if (!child_id_.is_valid() || size_changed) { |
| if (child_id_.is_valid()) |
| DestroySurface(); |
| AllocateSurface(); |
| } |
| |
| support_->SubmitCompositorFrame(child_id_, |
| std::move(*child_compositor_frame)); |
| } |
| |
| gfx::Transform transform(gfx::Transform::kSkipInitialization); |
| transform.matrix().setColMajorf(draw_info->transform); |
| transform.Translate(scroll_offset_.x(), scroll_offset_.y()); |
| |
| gfx::Size viewport(draw_info->width, draw_info->height); |
| // Need to post the new transform matrix back to child compositor |
| // because there is no onDraw during a Render Thread animation, and child |
| // compositor might not have the tiles rasterized as the animation goes on. |
| ParentCompositorDrawConstraints draw_constraints( |
| draw_info->is_layer, transform, viewport.IsEmpty()); |
| if (!child_frame_.get() || draw_constraints.NeedUpdate(*child_frame_)) { |
| render_thread_manager_->PostExternalDrawConstraintsToChildCompositorOnRT( |
| draw_constraints); |
| } |
| |
| if (!child_id_.is_valid()) |
| return; |
| |
| gfx::Rect clip(draw_info->clip_left, draw_info->clip_top, |
| draw_info->clip_right - draw_info->clip_left, |
| draw_info->clip_bottom - draw_info->clip_top); |
| surfaces_->DrawAndSwap(viewport, clip, transform, frame_size_, |
| cc::SurfaceId(frame_sink_id_, child_id_)); |
| } |
| |
| void HardwareRenderer::AllocateSurface() { |
| DCHECK(!child_id_.is_valid()); |
| child_id_ = local_surface_id_allocator_->GenerateId(); |
| surfaces_->AddChildId(cc::SurfaceId(frame_sink_id_, child_id_)); |
| } |
| |
| void HardwareRenderer::DestroySurface() { |
| DCHECK(child_id_.is_valid()); |
| |
| // Submit an empty frame to force any existing resources to be returned. |
| support_->SubmitCompositorFrame(child_id_, cc::CompositorFrame()); |
| surfaces_->RemoveChildId(cc::SurfaceId(frame_sink_id_, child_id_)); |
| support_->EvictFrame(); |
| child_id_ = cc::LocalSurfaceId(); |
| } |
| |
| void HardwareRenderer::DidReceiveCompositorFrameAck() {} |
| |
| void HardwareRenderer::OnBeginFrame(const cc::BeginFrameArgs& args) { |
| // TODO(tansell): Hook this up. |
| } |
| |
| void HardwareRenderer::ReclaimResources( |
| const cc::ReturnedResourceArray& resources) { |
| ReturnResourcesToCompositor(resources, compositor_id_, |
| last_submitted_compositor_frame_sink_id_); |
| } |
| |
| void HardwareRenderer::WillDrawSurface( |
| const cc::LocalSurfaceId& local_surface_id, |
| const gfx::Rect& damage_rect) {} |
| |
| // static |
| ChildFrameQueue HardwareRenderer::WaitAndPruneFrameQueue( |
| ChildFrameQueue* child_frames_ptr) { |
| ChildFrameQueue& child_frames = *child_frames_ptr; |
| ChildFrameQueue pruned_frames; |
| |
| // First find the last non-empty frame. |
| int last_non_empty_index = -1; |
| for (size_t i = 0; i < child_frames.size(); ++i) { |
| auto& child_frame = *child_frames[i]; |
| child_frame.WaitOnFutureIfNeeded(); |
| if (child_frame.frame) |
| last_non_empty_index = i; |
| } |
| if (last_non_empty_index < 0) { |
| child_frames.clear(); |
| return pruned_frames; |
| } |
| |
| // Prune end. |
| while (child_frames.size() > static_cast<size_t>(last_non_empty_index + 1)) { |
| std::unique_ptr<ChildFrame> frame = std::move(child_frames.back()); |
| child_frames.pop_back(); |
| if (frame->frame) |
| pruned_frames.emplace_back(std::move(frame)); |
| } |
| |
| // Prune front. |
| while (child_frames.size() > 1) { |
| std::unique_ptr<ChildFrame> frame = std::move(child_frames.front()); |
| child_frames.pop_front(); |
| if (frame->frame) |
| pruned_frames.emplace_back(std::move(frame)); |
| } |
| return pruned_frames; |
| } |
| |
| void HardwareRenderer::ReturnChildFrame( |
| std::unique_ptr<ChildFrame> child_frame) { |
| if (!child_frame || !child_frame->frame) |
| return; |
| |
| cc::ReturnedResourceArray resources_to_return; |
| cc::TransferableResource::ReturnResources(child_frame->frame->resource_list, |
| &resources_to_return); |
| |
| // The child frame's compositor id is not necessarily same as |
| // compositor_id_. |
| ReturnResourcesToCompositor(resources_to_return, child_frame->compositor_id, |
| child_frame->compositor_frame_sink_id); |
| } |
| |
| void HardwareRenderer::ReturnResourcesToCompositor( |
| const cc::ReturnedResourceArray& resources, |
| const CompositorID& compositor_id, |
| uint32_t compositor_frame_sink_id) { |
| if (compositor_frame_sink_id != last_committed_compositor_frame_sink_id_) |
| return; |
| render_thread_manager_->InsertReturnedResourcesOnRT(resources, compositor_id, |
| compositor_frame_sink_id); |
| } |
| |
| void HardwareRenderer::CreateNewCompositorFrameSinkSupport() { |
| support_.reset(); |
| support_ = base::MakeUnique<cc::CompositorFrameSinkSupport>( |
| this, surfaces_->GetSurfaceManager(), frame_sink_id_, false /* is_root */, |
| false /* handles_frame_sink_id_invalidation */, |
| true /* needs_sync_points */); |
| } |
| |
| } // namespace android_webview |