// Copyright (c) 2013 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/output_surface.h" #include #include #include #include #include "base/bind.h" #include "base/debug/trace_event.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "cc/output/compositor_frame.h" #include "cc/output/compositor_frame_ack.h" #include "cc/output/managed_memory_policy.h" #include "cc/output/output_surface_client.h" #include "cc/scheduler/delay_based_time_source.h" #include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/context_support.h" #include "gpu/command_buffer/client/gles2_interface.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3D.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" #include "ui/gfx/frame_time.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" using std::set; using std::string; using std::vector; namespace { const size_t kGpuLatencyHistorySize = 60; const double kGpuLatencyEstimationPercentile = 100.0; } namespace cc { OutputSurface::OutputSurface(scoped_refptr context_provider) : context_provider_(context_provider), device_scale_factor_(-1), max_frames_pending_(0), pending_swap_buffers_(0), needs_begin_impl_frame_(false), client_ready_for_begin_impl_frame_(true), client_(NULL), check_for_retroactive_begin_impl_frame_pending_(false), external_stencil_test_enabled_(false), weak_ptr_factory_(this), gpu_latency_history_(kGpuLatencyHistorySize) {} OutputSurface::OutputSurface(scoped_ptr software_device) : software_device_(software_device.Pass()), device_scale_factor_(-1), max_frames_pending_(0), pending_swap_buffers_(0), needs_begin_impl_frame_(false), client_ready_for_begin_impl_frame_(true), client_(NULL), check_for_retroactive_begin_impl_frame_pending_(false), external_stencil_test_enabled_(false), weak_ptr_factory_(this), gpu_latency_history_(kGpuLatencyHistorySize) {} OutputSurface::OutputSurface(scoped_refptr context_provider, scoped_ptr software_device) : context_provider_(context_provider), software_device_(software_device.Pass()), device_scale_factor_(-1), max_frames_pending_(0), pending_swap_buffers_(0), needs_begin_impl_frame_(false), client_ready_for_begin_impl_frame_(true), client_(NULL), check_for_retroactive_begin_impl_frame_pending_(false), external_stencil_test_enabled_(false), weak_ptr_factory_(this), gpu_latency_history_(kGpuLatencyHistorySize) {} void OutputSurface::InitializeBeginImplFrameEmulation( base::SingleThreadTaskRunner* task_runner, bool throttle_frame_production, base::TimeDelta interval) { if (throttle_frame_production) { scoped_refptr time_source; if (gfx::FrameTime::TimestampsAreHighRes()) time_source = DelayBasedTimeSourceHighRes::Create(interval, task_runner); else time_source = DelayBasedTimeSource::Create(interval, task_runner); frame_rate_controller_.reset(new FrameRateController(time_source)); } else { frame_rate_controller_.reset(new FrameRateController(task_runner)); } frame_rate_controller_->SetClient(this); frame_rate_controller_->SetMaxSwapsPending(max_frames_pending_); frame_rate_controller_->SetDeadlineAdjustment( capabilities_.adjust_deadline_for_parent ? BeginFrameArgs::DefaultDeadlineAdjustment() : base::TimeDelta()); // The new frame rate controller will consume the swap acks of the old // frame rate controller, so we set that expectation up here. for (int i = 0; i < pending_swap_buffers_; i++) frame_rate_controller_->DidSwapBuffers(); } void OutputSurface::SetMaxFramesPending(int max_frames_pending) { if (frame_rate_controller_) frame_rate_controller_->SetMaxSwapsPending(max_frames_pending); max_frames_pending_ = max_frames_pending; } void OutputSurface::OnVSyncParametersChanged(base::TimeTicks timebase, base::TimeDelta interval) { TRACE_EVENT2("cc", "OutputSurface::OnVSyncParametersChanged", "timebase", (timebase - base::TimeTicks()).InSecondsF(), "interval", interval.InSecondsF()); if (frame_rate_controller_) frame_rate_controller_->SetTimebaseAndInterval(timebase, interval); } void OutputSurface::FrameRateControllerTick(bool throttled, const BeginFrameArgs& args) { DCHECK(frame_rate_controller_); if (throttled) skipped_begin_impl_frame_args_ = args; else BeginImplFrame(args); } // Forwarded to OutputSurfaceClient void OutputSurface::SetNeedsRedrawRect(gfx::Rect damage_rect) { TRACE_EVENT0("cc", "OutputSurface::SetNeedsRedrawRect"); client_->SetNeedsRedrawRect(damage_rect); } void OutputSurface::SetNeedsBeginImplFrame(bool enable) { TRACE_EVENT1("cc", "OutputSurface::SetNeedsBeginImplFrame", "enable", enable); needs_begin_impl_frame_ = enable; client_ready_for_begin_impl_frame_ = true; if (frame_rate_controller_) { BeginFrameArgs skipped = frame_rate_controller_->SetActive(enable); if (skipped.IsValid()) skipped_begin_impl_frame_args_ = skipped; } if (needs_begin_impl_frame_) PostCheckForRetroactiveBeginImplFrame(); } void OutputSurface::BeginImplFrame(const BeginFrameArgs& args) { TRACE_EVENT2("cc", "OutputSurface::BeginImplFrame", "client_ready_for_begin_impl_frame_", client_ready_for_begin_impl_frame_, "pending_swap_buffers_", pending_swap_buffers_); if (!needs_begin_impl_frame_ || !client_ready_for_begin_impl_frame_ || (pending_swap_buffers_ >= max_frames_pending_ && max_frames_pending_ > 0)) { skipped_begin_impl_frame_args_ = args; } else { client_ready_for_begin_impl_frame_ = false; client_->BeginImplFrame(args); // args might be an alias for skipped_begin_impl_frame_args_. // Do not reset it before calling BeginImplFrame! skipped_begin_impl_frame_args_ = BeginFrameArgs(); } } base::TimeTicks OutputSurface::RetroactiveBeginImplFrameDeadline() { // TODO(brianderson): Remove the alternative deadline once we have better // deadline estimations. base::TimeTicks alternative_deadline = skipped_begin_impl_frame_args_.frame_time + BeginFrameArgs::DefaultRetroactiveBeginFramePeriod(); return std::max(skipped_begin_impl_frame_args_.deadline, alternative_deadline); } void OutputSurface::PostCheckForRetroactiveBeginImplFrame() { if (!skipped_begin_impl_frame_args_.IsValid() || check_for_retroactive_begin_impl_frame_pending_) return; base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&OutputSurface::CheckForRetroactiveBeginImplFrame, weak_ptr_factory_.GetWeakPtr())); check_for_retroactive_begin_impl_frame_pending_ = true; } void OutputSurface::CheckForRetroactiveBeginImplFrame() { TRACE_EVENT0("cc", "OutputSurface::CheckForRetroactiveBeginImplFrame"); check_for_retroactive_begin_impl_frame_pending_ = false; if (gfx::FrameTime::Now() < RetroactiveBeginImplFrameDeadline()) BeginImplFrame(skipped_begin_impl_frame_args_); } void OutputSurface::DidSwapBuffers() { pending_swap_buffers_++; TRACE_EVENT1("cc", "OutputSurface::DidSwapBuffers", "pending_swap_buffers_", pending_swap_buffers_); client_->DidSwapBuffers(); if (frame_rate_controller_) frame_rate_controller_->DidSwapBuffers(); PostCheckForRetroactiveBeginImplFrame(); } void OutputSurface::OnSwapBuffersComplete() { pending_swap_buffers_--; TRACE_EVENT1("cc", "OutputSurface::OnSwapBuffersComplete", "pending_swap_buffers_", pending_swap_buffers_); client_->OnSwapBuffersComplete(); if (frame_rate_controller_) frame_rate_controller_->DidSwapBuffersComplete(); PostCheckForRetroactiveBeginImplFrame(); } void OutputSurface::ReclaimResources(const CompositorFrameAck* ack) { client_->ReclaimResources(ack); } void OutputSurface::DidLoseOutputSurface() { TRACE_EVENT0("cc", "OutputSurface::DidLoseOutputSurface"); client_ready_for_begin_impl_frame_ = true; pending_swap_buffers_ = 0; skipped_begin_impl_frame_args_ = BeginFrameArgs(); if (frame_rate_controller_) frame_rate_controller_->SetActive(false); pending_gpu_latency_query_ids_.clear(); available_gpu_latency_query_ids_.clear(); client_->DidLoseOutputSurface(); } void OutputSurface::SetExternalStencilTest(bool enabled) { external_stencil_test_enabled_ = enabled; } void OutputSurface::SetExternalDrawConstraints(const gfx::Transform& transform, gfx::Rect viewport, gfx::Rect clip, bool valid_for_tile_management) { client_->SetExternalDrawConstraints( transform, viewport, clip, valid_for_tile_management); } OutputSurface::~OutputSurface() { if (frame_rate_controller_) frame_rate_controller_->SetActive(false); ResetContext3d(); } bool OutputSurface::HasExternalStencilTest() const { return external_stencil_test_enabled_; } bool OutputSurface::ForcedDrawToSoftwareDevice() const { return false; } bool OutputSurface::BindToClient(OutputSurfaceClient* client) { DCHECK(client); client_ = client; bool success = true; if (context_provider_) { success = context_provider_->BindToCurrentThread(); if (success) SetUpContext3d(); } if (!success) client_ = NULL; return success; } bool OutputSurface::InitializeAndSetContext3d( scoped_refptr context_provider, scoped_refptr offscreen_context_provider) { DCHECK(!context_provider_); DCHECK(context_provider); DCHECK(client_); bool success = false; if (context_provider->BindToCurrentThread()) { context_provider_ = context_provider; SetUpContext3d(); if (client_->DeferredInitialize(offscreen_context_provider)) success = true; } if (!success) ResetContext3d(); return success; } void OutputSurface::ReleaseGL() { DCHECK(client_); DCHECK(context_provider_); client_->ReleaseGL(); ResetContext3d(); } void OutputSurface::SetUpContext3d() { DCHECK(context_provider_); DCHECK(client_); context_provider_->SetLostContextCallback( base::Bind(&OutputSurface::DidLoseOutputSurface, base::Unretained(this))); context_provider_->ContextSupport()->SetSwapBuffersCompleteCallback( base::Bind(&OutputSurface::OnSwapBuffersComplete, base::Unretained(this))); context_provider_->SetMemoryPolicyChangedCallback( base::Bind(&OutputSurface::SetMemoryPolicy, base::Unretained(this))); } void OutputSurface::ResetContext3d() { if (context_provider_.get()) { while (!pending_gpu_latency_query_ids_.empty()) { unsigned query_id = pending_gpu_latency_query_ids_.front(); pending_gpu_latency_query_ids_.pop_front(); context_provider_->Context3d()->deleteQueryEXT(query_id); } while (!available_gpu_latency_query_ids_.empty()) { unsigned query_id = available_gpu_latency_query_ids_.front(); available_gpu_latency_query_ids_.pop_front(); context_provider_->Context3d()->deleteQueryEXT(query_id); } context_provider_->SetLostContextCallback( ContextProvider::LostContextCallback()); context_provider_->SetMemoryPolicyChangedCallback( ContextProvider::MemoryPolicyChangedCallback()); if (gpu::ContextSupport* support = context_provider_->ContextSupport()) support->SetSwapBuffersCompleteCallback(base::Closure()); } context_provider_ = NULL; } void OutputSurface::EnsureBackbuffer() { if (software_device_) software_device_->EnsureBackbuffer(); } void OutputSurface::DiscardBackbuffer() { if (context_provider_) context_provider_->ContextGL()->DiscardBackbufferCHROMIUM(); if (software_device_) software_device_->DiscardBackbuffer(); } void OutputSurface::Reshape(gfx::Size size, float scale_factor) { if (size == surface_size_ && scale_factor == device_scale_factor_) return; surface_size_ = size; device_scale_factor_ = scale_factor; if (context_provider_) { context_provider_->Context3d()->reshapeWithScaleFactor( size.width(), size.height(), scale_factor); } if (software_device_) software_device_->Resize(size); } gfx::Size OutputSurface::SurfaceSize() const { return surface_size_; } void OutputSurface::BindFramebuffer() { DCHECK(context_provider_); context_provider_->Context3d()->bindFramebuffer(GL_FRAMEBUFFER, 0); } void OutputSurface::SwapBuffers(CompositorFrame* frame) { if (frame->software_frame_data) { PostSwapBuffersComplete(); DidSwapBuffers(); return; } DCHECK(context_provider_); DCHECK(frame->gl_frame_data); UpdateAndMeasureGpuLatency(); if (frame->gl_frame_data->sub_buffer_rect == gfx::Rect(frame->gl_frame_data->size)) { context_provider_->ContextSupport()->Swap(); } else { context_provider_->ContextSupport()->PartialSwapBuffers( frame->gl_frame_data->sub_buffer_rect); } DidSwapBuffers(); } base::TimeDelta OutputSurface::GpuLatencyEstimate() { if (context_provider_ && !capabilities_.adjust_deadline_for_parent) return gpu_latency_history_.Percentile(kGpuLatencyEstimationPercentile); else return base::TimeDelta(); } void OutputSurface::UpdateAndMeasureGpuLatency() { return; // http://crbug.com/306690 tracks re-enabling latency queries. // We only care about GPU latency for surfaces that do not have a parent // compositor, since surfaces that do have a parent compositor can use // mailboxes or delegated rendering to send frames to their parent without // incurring GPU latency. if (capabilities_.adjust_deadline_for_parent) return; while (pending_gpu_latency_query_ids_.size()) { unsigned query_id = pending_gpu_latency_query_ids_.front(); unsigned query_complete = 1; context_provider_->Context3d()->getQueryObjectuivEXT( query_id, GL_QUERY_RESULT_AVAILABLE_EXT, &query_complete); if (!query_complete) break; unsigned value = 0; context_provider_->Context3d()->getQueryObjectuivEXT( query_id, GL_QUERY_RESULT_EXT, &value); pending_gpu_latency_query_ids_.pop_front(); available_gpu_latency_query_ids_.push_back(query_id); base::TimeDelta latency = base::TimeDelta::FromMicroseconds(value); base::TimeDelta latency_estimate = GpuLatencyEstimate(); gpu_latency_history_.InsertSample(latency); base::TimeDelta latency_overestimate; base::TimeDelta latency_underestimate; if (latency > latency_estimate) latency_underestimate = latency - latency_estimate; else latency_overestimate = latency_estimate - latency; UMA_HISTOGRAM_CUSTOM_TIMES("Renderer.GpuLatency", latency, base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMilliseconds(100), 50); UMA_HISTOGRAM_CUSTOM_TIMES("Renderer.GpuLatencyUnderestimate", latency_underestimate, base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMilliseconds(100), 50); UMA_HISTOGRAM_CUSTOM_TIMES("Renderer.GpuLatencyOverestimate", latency_overestimate, base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMilliseconds(100), 50); } unsigned gpu_latency_query_id; if (available_gpu_latency_query_ids_.size()) { gpu_latency_query_id = available_gpu_latency_query_ids_.front(); available_gpu_latency_query_ids_.pop_front(); } else { gpu_latency_query_id = context_provider_->Context3d()->createQueryEXT(); } context_provider_->Context3d()->beginQueryEXT(GL_LATENCY_QUERY_CHROMIUM, gpu_latency_query_id); context_provider_->Context3d()->endQueryEXT(GL_LATENCY_QUERY_CHROMIUM); pending_gpu_latency_query_ids_.push_back(gpu_latency_query_id); } void OutputSurface::PostSwapBuffersComplete() { base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&OutputSurface::OnSwapBuffersComplete, weak_ptr_factory_.GetWeakPtr())); } void OutputSurface::SetMemoryPolicy(const ManagedMemoryPolicy& policy) { TRACE_EVENT1("cc", "OutputSurface::SetMemoryPolicy", "bytes_limit_when_visible", policy.bytes_limit_when_visible); // Just ignore the memory manager when it says to set the limit to zero // bytes. This will happen when the memory manager thinks that the renderer // is not visible (which the renderer knows better). if (policy.bytes_limit_when_visible) client_->SetMemoryPolicy(policy); } } // namespace cc