blob: 37a51e4a264736907239b7b4dc824e07670937d3 [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 "services/gfx/compositor/backend/gpu_output.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/trace_event/trace_event.h"
#include "services/gfx/compositor/backend/gpu_rasterizer.h"
#include "services/gfx/compositor/render/render_frame.h"
namespace compositor {
namespace {
// Maximum number of frames to hold in the queue for rendering.
constexpr size_t kMaxPipelineDepth = 1;
}
template <typename T>
static void Drop(scoped_ptr<T> ptr) {}
static scoped_ptr<base::MessagePump> CreateMessagePumpMojo() {
return base::MessageLoop::CreateMessagePumpForType(
base::MessageLoop::TYPE_DEFAULT);
}
GpuOutput::GpuOutput(
mojo::InterfaceHandle<mojo::ContextProvider> context_provider,
const SchedulerCallbacks& scheduler_callbacks,
const base::Closure& error_callback)
: scheduler_(new VsyncScheduler(base::MessageLoop::current()->task_runner(),
scheduler_callbacks)),
rasterizer_delegate_(make_scoped_ptr(new RasterizerDelegate())) {
DCHECK(context_provider);
rasterizer_delegate_->PostInitialize(
std::move(context_provider), scheduler_,
base::MessageLoop::current()->task_runner(), error_callback);
}
GpuOutput::~GpuOutput() {
// Ensure destruction happens on the correct thread.
rasterizer_delegate_->PostDestroy(rasterizer_delegate_.Pass());
}
Scheduler* GpuOutput::GetScheduler() {
return scheduler_.get();
}
void GpuOutput::SubmitFrame(const scoped_refptr<RenderFrame>& frame) {
rasterizer_delegate_->PostFrame(frame);
}
GpuOutput::RasterizerDelegate::RasterizerDelegate() {
base::Thread::Options options;
options.message_pump_factory = base::Bind(&CreateMessagePumpMojo);
thread_.reset(new base::Thread("gpu_rasterizer"));
thread_->StartWithOptions(options);
task_runner_ = thread_->message_loop()->task_runner();
}
GpuOutput::RasterizerDelegate::~RasterizerDelegate() {}
void GpuOutput::RasterizerDelegate::PostInitialize(
mojo::InterfaceHandle<mojo::ContextProvider> context_provider,
const scoped_refptr<VsyncScheduler>& scheduler,
const scoped_refptr<base::TaskRunner>& task_runner,
const base::Closure& error_callback) {
task_runner_->PostTask(
FROM_HERE,
base::Bind(&RasterizerDelegate::InitializeTask, base::Unretained(this),
base::Passed(std::move(context_provider)), scheduler,
base::MessageLoop::current()->task_runner(), error_callback));
}
void GpuOutput::RasterizerDelegate::PostDestroy(
scoped_ptr<RasterizerDelegate> self) {
task_runner_->PostTask(
FROM_HERE, base::Bind(&Drop<RasterizerDelegate>, base::Passed(&self)));
}
void GpuOutput::RasterizerDelegate::PostFrame(
const scoped_refptr<RenderFrame>& frame) {
bool was_empty;
scoped_refptr<RenderFrame> dropped_frame;
{
std::lock_guard<std::mutex> lock(mutex_);
was_empty = frames_.empty();
if (frames_.size() == kMaxPipelineDepth) {
// TODO(jeffbrown): Adjust scheduler behavior to compensate.
LOG(ERROR) << "Renderer pipeline stalled, dropping a frame to catch up.";
dropped_frame = frames_.front(); // drop an old frame outside the lock
frames_.pop();
}
frames_.push(frame);
}
if (was_empty)
PostSubmit();
}
void GpuOutput::RasterizerDelegate::PostSubmit() {
TRACE_EVENT0("gfx", "GpuOutput::RasterizerDelegate::PostSubmit");
task_runner_->PostTask(FROM_HERE, base::Bind(&RasterizerDelegate::SubmitTask,
base::Unretained(this)));
}
void GpuOutput::RasterizerDelegate::InitializeTask(
mojo::InterfaceHandle<mojo::ContextProvider> context_provider,
const scoped_refptr<VsyncScheduler>& scheduler,
const scoped_refptr<base::TaskRunner>& task_runner,
const base::Closure& error_callback) {
rasterizer_.reset(new GpuRasterizer(
mojo::ContextProviderPtr::Create(std::move(context_provider)), scheduler,
task_runner, error_callback));
}
void GpuOutput::RasterizerDelegate::SubmitTask() {
TRACE_EVENT0("gfx", "GpuOutput::RasterizerDelegate::SubmitTask");
bool have_more;
scoped_refptr<RenderFrame> frame;
{
std::lock_guard<std::mutex> lock(mutex_);
DCHECK(!frames_.empty());
frame = frames_.front();
frames_.pop();
have_more = !frames_.empty();
}
if (have_more)
PostSubmit();
int64_t submit_time = MojoGetTimeTicksNow();
rasterizer_->SubmitFrame(
frame, base::Bind(&RasterizerDelegate::OnFrameSubmitted,
base::Unretained(this), frame->frame_info().frame_time,
frame->frame_info().presentation_time, submit_time));
}
void GpuOutput::RasterizerDelegate::OnFrameSubmitted(int64_t frame_time,
int64_t presentation_time,
int64_t submit_time,
bool presented) {
TRACE_EVENT0("gfx", "GpuOutput::RasterizerDelegate::OnFrameSubmitted");
// TODO(jeffbrown): Adjust scheduler behavior based on observed timing.
// Note: These measurements don't account for systematic downstream delay
// in the display pipeline (how long it takes pixels to actually light up).
int64_t complete_time = MojoGetTimeTicksNow();
if (presented) {
DVLOG(3) << "Frame presented: submission latency "
<< (submit_time - frame_time) << " us, rasterization latency "
<< (complete_time - submit_time) << " us, total latency "
<< (complete_time - frame_time) << " us, presentation time error "
<< (complete_time - presentation_time);
} else {
DVLOG(3) << "Frame deferred.";
}
}
} // namespace compositor