| // 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 |