blob: bdd6aa35ddced4f2c24764a5ca7195d5363b90e6 [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_rasterizer.h"
#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#endif
#include <GLES2/gl2.h>
#include <GLES2/gl2extmojo.h>
#include <MGL/mgl.h>
#include <MGL/mgl_onscreen.h>
#include "base/bind.h"
#include "base/location.h"
#include "base/logging.h"
#include "services/gfx/compositor/backend/vsync_scheduler.h"
#include "services/gfx/compositor/render/render_frame.h"
namespace compositor {
GpuRasterizer::GpuRasterizer(mojo::ContextProviderPtr context_provider,
const std::shared_ptr<VsyncScheduler>& scheduler,
const scoped_refptr<base::TaskRunner>& task_runner,
const base::Closure& error_callback)
: context_provider_(context_provider.Pass()),
scheduler_(scheduler),
task_runner_(task_runner),
error_callback_(error_callback),
viewport_parameter_listener_binding_(this) {
DCHECK(context_provider_);
context_provider_.set_connection_error_handler(
base::Bind(&GpuRasterizer::OnContextProviderConnectionError,
base::Unretained(this)));
CreateContext();
}
GpuRasterizer::~GpuRasterizer() {
DestroyContext();
}
void GpuRasterizer::CreateContext() {
mojo::ViewportParameterListenerPtr viewport_parameter_listener;
viewport_parameter_listener_binding_.Bind(
GetProxy(&viewport_parameter_listener));
context_provider_->Create(
viewport_parameter_listener.Pass(),
base::Bind(&GpuRasterizer::InitContext, base::Unretained(this)));
}
void GpuRasterizer::InitContext(mojo::CommandBufferPtr command_buffer) {
DCHECK(!gl_context_);
DCHECK(!ganesh_context_);
DCHECK(!ganesh_surface_);
if (!command_buffer) {
LOG(ERROR) << "Could not create GL context.";
PostErrorCallback();
return;
}
gl_context_ = mojo::GLContext::CreateFromCommandBuffer(command_buffer.Pass());
gl_context_->AddObserver(this);
ganesh_context_.reset(new mojo::skia::GaneshContext(gl_context_));
if (current_frame_)
Draw();
}
void GpuRasterizer::DestroyContext() {
if (gl_context_) {
scheduler_->Stop();
// Release objects that belong to special scopes.
{
mojo::skia::GaneshContext::Scope ganesh_scope(ganesh_context_.get());
ganesh_surface_.reset();
}
// Release the ganesh context before the GL context.
ganesh_context_.reset();
// Now release the GL context.
gl_context_->Destroy();
gl_context_.reset();
}
}
void GpuRasterizer::OnContextProviderConnectionError() {
LOG(ERROR) << "Context provider connection lost.";
PostErrorCallback();
}
void GpuRasterizer::OnContextLost() {
LOG(WARNING) << "GL context lost, recreating it.";
DestroyContext();
CreateContext();
}
void GpuRasterizer::OnVSyncParametersUpdated(int64_t timebase,
int64_t interval) {
DVLOG(1) << "Vsync parameters: timebase=" << timebase
<< ", interval=" << interval;
if (!gl_context_)
return;
// TODO(jeffbrown): This shouldn't be hardcoded.
// Need to do some real tuning and possibly determine values adaptively.
int64_t update_phase = -interval;
int64_t snapshot_phase = -interval / 8;
int64_t presentation_phase = interval;
if (!scheduler_->Start(timebase, interval, update_phase, snapshot_phase,
presentation_phase)) {
LOG(ERROR) << "Received invalid vsync parameters: timebase=" << timebase
<< ", interval=" << interval;
PostErrorCallback();
}
}
void GpuRasterizer::SubmitFrame(const std::shared_ptr<RenderFrame>& frame) {
DCHECK(frame);
if (current_frame_ == frame)
return;
current_frame_ = frame;
if (gl_context_)
Draw();
}
void GpuRasterizer::Draw() {
DCHECK(gl_context_);
DCHECK(ganesh_context_);
DCHECK(current_frame_);
gl_context_->MakeCurrent();
// Update the viewport.
const SkRect& viewport = current_frame_->viewport();
bool stale_surface = false;
if (!ganesh_surface_ ||
ganesh_surface_->surface()->width() != viewport.width() ||
ganesh_surface_->surface()->height() != viewport.height()) {
glViewport(viewport.x(), viewport.y(), viewport.width(), viewport.height());
glResizeCHROMIUM(viewport.width(), viewport.height(), 1.0f);
stale_surface = true;
}
// Paint the frame.
{
mojo::skia::GaneshContext::Scope ganesh_scope(ganesh_context_.get());
if (stale_surface)
ganesh_surface_.reset(
new mojo::skia::GaneshFramebufferSurface(ganesh_scope));
current_frame_->Paint(ganesh_surface_->canvas());
}
// Swap buffers.
MGLSwapBuffers();
}
void GpuRasterizer::PostErrorCallback() {
task_runner_->PostTask(FROM_HERE, error_callback_);
}
} // namespace compositor