blob: 1510f90363d2421b3b4606985bd361d74228f48b [file] [log] [blame]
// Copyright (c) 2012 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.
#if defined(ENABLE_GPU)
#include "content/common/gpu/image_transport_surface.h"
// Out of order because it has conflicts with other includes on Windows.
#include "third_party/angle/include/EGL/egl.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/win/windows_version.h"
#include "content/common/gpu/gpu_messages.h"
#include "content/public/common/content_switches.h"
#include "content/common/gpu/texture_image_transport_surface.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_context.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_surface_egl.h"
namespace {
// We are backed by an Pbuffer offscreen surface through which ANGLE provides
// a handle to the corresponding render target texture through an extension.
class PbufferImageTransportSurface
: public gfx::GLSurfaceAdapter,
public ImageTransportSurface,
public base::SupportsWeakPtr<PbufferImageTransportSurface> {
public:
PbufferImageTransportSurface(GpuChannelManager* manager,
GpuCommandBufferStub* stub);
// gfx::GLSurface implementation
virtual bool Initialize() OVERRIDE;
virtual void Destroy() OVERRIDE;
virtual bool DeferDraws() OVERRIDE;
virtual bool IsOffscreen() OVERRIDE;
virtual bool SwapBuffers() OVERRIDE;
virtual bool PostSubBuffer(int x, int y, int width, int height) OVERRIDE;
virtual std::string GetExtensions() OVERRIDE;
virtual void SetBackbufferAllocation(bool allocated) OVERRIDE;
virtual void SetFrontbufferAllocation(bool allocated) OVERRIDE;
protected:
// ImageTransportSurface implementation
virtual void OnBufferPresented(uint32 sync_point) OVERRIDE;
virtual void OnResizeViewACK() OVERRIDE;
virtual void OnResize(gfx::Size size) OVERRIDE;
virtual gfx::Size GetSize() OVERRIDE;
private:
virtual ~PbufferImageTransportSurface();
void SendBuffersSwapped();
void DestroySurface();
// Tracks the current buffer allocation state.
bool backbuffer_suggested_allocation_;
bool frontbuffer_suggested_allocation_;
// Whether a SwapBuffers is pending.
bool is_swap_buffers_pending_;
// Whether we unscheduled command buffer because of pending SwapBuffers.
bool did_unschedule_;
// Size to resize to when the surface becomes visible.
gfx::Size visible_size_;
scoped_ptr<ImageTransportHelper> helper_;
DISALLOW_COPY_AND_ASSIGN(PbufferImageTransportSurface);
};
PbufferImageTransportSurface::PbufferImageTransportSurface(
GpuChannelManager* manager,
GpuCommandBufferStub* stub)
: GLSurfaceAdapter(new gfx::PbufferGLSurfaceEGL(false, gfx::Size(1, 1))),
backbuffer_suggested_allocation_(true),
frontbuffer_suggested_allocation_(true),
is_swap_buffers_pending_(false),
did_unschedule_(false) {
helper_.reset(new ImageTransportHelper(this,
manager,
stub,
gfx::kNullPluginWindow));
}
PbufferImageTransportSurface::~PbufferImageTransportSurface() {
Destroy();
}
bool PbufferImageTransportSurface::Initialize() {
// Only support this path if the GL implementation is ANGLE.
// IO surfaces will not work with, for example, OSMesa software renderer
// GL contexts.
if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2)
return false;
if (!helper_->Initialize())
return false;
return GLSurfaceAdapter::Initialize();
}
void PbufferImageTransportSurface::Destroy() {
helper_->Destroy();
GLSurfaceAdapter::Destroy();
}
bool PbufferImageTransportSurface::DeferDraws() {
// The command buffer hit a draw/clear command that could clobber the
// IOSurface in use by an earlier SwapBuffers. If a Swap is pending, abort
// processing of the command by returning true and unschedule until the Swap
// Ack arrives.
if (did_unschedule_)
return true;
if (is_swap_buffers_pending_) {
did_unschedule_ = true;
helper_->SetScheduled(false);
return true;
}
return false;
}
bool PbufferImageTransportSurface::IsOffscreen() {
return false;
}
bool PbufferImageTransportSurface::SwapBuffers() {
DCHECK(backbuffer_suggested_allocation_);
if (!frontbuffer_suggested_allocation_)
return true;
HANDLE surface_handle = GetShareHandle();
if (!surface_handle)
return false;
// Don't send the surface to the browser until we hit the fence that
// indicates the drawing to the surface has been completed.
// TODO(jbates) unscheduling should be deferred until draw commands from the
// next frame -- otherwise the GPU is potentially sitting idle.
helper_->DeferToFence(base::Bind(
&PbufferImageTransportSurface::SendBuffersSwapped,
AsWeakPtr()));
return true;
}
bool PbufferImageTransportSurface::PostSubBuffer(
int x, int y, int width, int height) {
NOTREACHED();
return false;
}
void PbufferImageTransportSurface::SetBackbufferAllocation(bool allocation) {
if (backbuffer_suggested_allocation_ == allocation)
return;
backbuffer_suggested_allocation_ = allocation;
if (backbuffer_suggested_allocation_)
Resize(visible_size_);
else
Resize(gfx::Size(1, 1));
DestroySurface();
}
void PbufferImageTransportSurface::SetFrontbufferAllocation(bool allocation) {
if (frontbuffer_suggested_allocation_ == allocation)
return;
frontbuffer_suggested_allocation_ = allocation;
// We recreate frontbuffer by recreating backbuffer and swapping.
// But we release frontbuffer by telling UI to release its handle on it.
if (!frontbuffer_suggested_allocation_)
helper_->Suspend();
}
void PbufferImageTransportSurface::DestroySurface() {
GpuHostMsg_AcceleratedSurfaceRelease_Params params;
helper_->SendAcceleratedSurfaceRelease(params);
}
std::string PbufferImageTransportSurface::GetExtensions() {
std::string extensions = gfx::GLSurface::GetExtensions();
extensions += extensions.empty() ? "" : " ";
extensions += "GL_CHROMIUM_front_buffer_cached";
return extensions;
}
void PbufferImageTransportSurface::SendBuffersSwapped() {
GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params;
params.surface_handle = reinterpret_cast<int64>(GetShareHandle());
CHECK(params.surface_handle);
helper_->SendAcceleratedSurfaceBuffersSwapped(params);
DCHECK(!is_swap_buffers_pending_);
is_swap_buffers_pending_ = true;
}
void PbufferImageTransportSurface::OnBufferPresented(uint32 sync_point) {
is_swap_buffers_pending_ = false;
if (did_unschedule_) {
did_unschedule_ = false;
helper_->SetScheduled(true);
}
}
void PbufferImageTransportSurface::OnResizeViewACK() {
NOTREACHED();
}
void PbufferImageTransportSurface::OnResize(gfx::Size size) {
DCHECK(backbuffer_suggested_allocation_);
DCHECK(frontbuffer_suggested_allocation_);
Resize(size);
DestroySurface();
visible_size_ = size;
}
gfx::Size PbufferImageTransportSurface::GetSize() {
return GLSurfaceAdapter::GetSize();
}
} // namespace anonymous
// static
scoped_refptr<gfx::GLSurface> ImageTransportSurface::CreateSurface(
GpuChannelManager* manager,
GpuCommandBufferStub* stub,
const gfx::GLSurfaceHandle& handle) {
scoped_refptr<gfx::GLSurface> surface;
if (!handle.handle) {
// If we don't have a valid handle with the transport flag set, then we're
// coming from a renderer and we want to render the webpage contents to a
// texture.
DCHECK(handle.transport);
DCHECK(handle.parent_client_id);
surface = new TextureImageTransportSurface(manager, stub, handle);
} else {
if (handle.transport &&
gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2 &&
!CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableImageTransportSurface)) {
// This path handles two different cases.
//
// For post-Vista regular Windows, this surface will be used for
// renderer compositors.
//
// For Aura Windows, this will be the surface for the browser compositor
// (and the renderer compositors surface's will be
// TextureImageTransportSurface above).
const char* extensions = eglQueryString(
eglGetDisplay(EGL_DEFAULT_DISPLAY), EGL_EXTENSIONS);
if (strstr(extensions, "EGL_ANGLE_query_surface_pointer") &&
strstr(extensions, "EGL_ANGLE_surface_d3d_texture_2d_share_handle")) {
surface = new PbufferImageTransportSurface(manager, stub);
}
}
if (!surface.get()) {
surface = gfx::GLSurface::CreateViewGLSurface(false, handle.handle);
if (!surface.get())
return NULL;
surface = new PassThroughImageTransportSurface(manager,
stub,
surface.get(),
handle.transport);
}
}
if (surface->Initialize())
return surface;
else
return NULL;
}
#endif // ENABLE_GPU