diff options
author | Torne (Richard Coles) <torne@google.com> | 2014-08-19 13:00:08 +0100 |
---|---|---|
committer | Torne (Richard Coles) <torne@google.com> | 2014-08-19 13:00:08 +0100 |
commit | 6e8cce623b6e4fe0c9e4af605d675dd9d0338c38 (patch) | |
tree | 8d824ad26fac42e008142b86aa9631b2be7e4705 /content/common/gpu/image_transport_surface_fbo_mac.mm | |
parent | 4f7316adb45db5ec3c9c1181ba9510c004566df8 (diff) | |
download | chromium_org-6e8cce623b6e4fe0c9e4af605d675dd9d0338c38.tar.gz |
Merge from Chromium at DEPS revision 290040
This commit was generated by merge_to_master.py.
Change-Id: I694ec52d1e0b553f163c2faf4373d63270ab1aac
Diffstat (limited to 'content/common/gpu/image_transport_surface_fbo_mac.mm')
-rw-r--r-- | content/common/gpu/image_transport_surface_fbo_mac.mm | 338 |
1 files changed, 338 insertions, 0 deletions
diff --git a/content/common/gpu/image_transport_surface_fbo_mac.mm b/content/common/gpu/image_transport_surface_fbo_mac.mm new file mode 100644 index 0000000000..c62205c470 --- /dev/null +++ b/content/common/gpu/image_transport_surface_fbo_mac.mm @@ -0,0 +1,338 @@ +// Copyright 2014 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 "content/common/gpu/image_transport_surface_fbo_mac.h" + +#include "content/common/gpu/gpu_messages.h" +#include "content/common/gpu/image_transport_surface_calayer_mac.h" +#include "content/common/gpu/image_transport_surface_iosurface_mac.h" +#include "ui/base/cocoa/remote_layer_api.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/gl_implementation.h" +#include "ui/gl/gl_surface_osmesa.h" + +namespace content { + +ImageTransportSurfaceFBO::ImageTransportSurfaceFBO( + GpuChannelManager* manager, + GpuCommandBufferStub* stub, + gfx::PluginWindowHandle handle) + : backbuffer_suggested_allocation_(true), + frontbuffer_suggested_allocation_(true), + fbo_id_(0), + texture_id_(0), + depth_stencil_renderbuffer_id_(0), + has_complete_framebuffer_(false), + context_(NULL), + scale_factor_(1.f), + made_current_(false), + is_swap_buffers_pending_(false), + did_unschedule_(false) { + if (ui::RemoteLayerAPISupported()) + storage_provider_.reset(new CALayerStorageProvider(this)); + else + storage_provider_.reset(new IOSurfaceStorageProvider(this)); + helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); +} + +ImageTransportSurfaceFBO::~ImageTransportSurfaceFBO() { +} + +bool ImageTransportSurfaceFBO::Initialize() { + // Only support IOSurfaces if the GL implementation is the native desktop GL. + // IO surfaces will not work with, for example, OSMesa software renderer + // GL contexts. + if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL && + gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL) + return false; + + if (!helper_->Initialize()) + return false; + + helper_->stub()->AddDestructionObserver(this); + return true; +} + +void ImageTransportSurfaceFBO::Destroy() { + DestroyFramebuffer(); + + helper_->Destroy(); +} + +bool ImageTransportSurfaceFBO::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; // Still unscheduled, so just return true. + if (is_swap_buffers_pending_) { + did_unschedule_ = true; + helper_->SetScheduled(false); + return true; + } + return false; +} + +bool ImageTransportSurfaceFBO::IsOffscreen() { + return false; +} + +bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) { + context_ = context; + + if (made_current_) + return true; + + OnResize(gfx::Size(1, 1), 1.f); + + made_current_ = true; + return true; +} + +unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() { + return fbo_id_; +} + +bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) { + if (backbuffer_suggested_allocation_ == allocation) + return true; + backbuffer_suggested_allocation_ = allocation; + AdjustBufferAllocation(); + return true; +} + +void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) { + if (frontbuffer_suggested_allocation_ == allocation) + return; + frontbuffer_suggested_allocation_ = allocation; + AdjustBufferAllocation(); +} + +void ImageTransportSurfaceFBO::AdjustBufferAllocation() { + // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is + // free'd when both the browser and gpu processes have Unref'd the IOSurface. + if (!backbuffer_suggested_allocation_ && + !frontbuffer_suggested_allocation_ && + has_complete_framebuffer_) { + DestroyFramebuffer(); + helper_->Suspend(); + } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) { + CreateFramebuffer(); + } +} + +bool ImageTransportSurfaceFBO::SwapBuffers() { + DCHECK(backbuffer_suggested_allocation_); + if (!frontbuffer_suggested_allocation_) + return true; + glFlush(); + + GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; + params.surface_handle = storage_provider_->GetSurfaceHandle(); + params.size = GetSize(); + params.scale_factor = scale_factor_; + params.latency_info.swap(latency_info_); + helper_->SendAcceleratedSurfaceBuffersSwapped(params); + + DCHECK(!is_swap_buffers_pending_); + is_swap_buffers_pending_ = true; + + storage_provider_->WillSwapBuffers(); + return true; +} + +bool ImageTransportSurfaceFBO::PostSubBuffer( + int x, int y, int width, int height) { + // Mac does not support sub-buffer swaps. + NOTREACHED(); + return false; +} + +bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() { + return true; +} + +gfx::Size ImageTransportSurfaceFBO::GetSize() { + return size_; +} + +void* ImageTransportSurfaceFBO::GetHandle() { + return NULL; +} + +void* ImageTransportSurfaceFBO::GetDisplay() { + return NULL; +} + +void ImageTransportSurfaceFBO::OnBufferPresented( + const AcceleratedSurfaceMsg_BufferPresented_Params& params) { + context_->share_group()->SetRendererID(params.renderer_id); + storage_provider_->CanFreeSwappedBuffer(); +} + +void ImageTransportSurfaceFBO::UnblockContextAfterPendingSwap() { + DCHECK(is_swap_buffers_pending_); + is_swap_buffers_pending_ = false; + if (did_unschedule_) { + did_unschedule_ = false; + helper_->SetScheduled(true); + } +} + +void ImageTransportSurfaceFBO::OnResize(gfx::Size size, + float scale_factor) { + // This trace event is used in gpu_feature_browsertest.cc - the test will need + // to be updated if this event is changed or moved. + TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize", + "old_width", size_.width(), "new_width", size.width()); + // Caching |context_| from OnMakeCurrent. It should still be current. + DCHECK(context_->IsCurrent(this)); + + size_ = size; + scale_factor_ = scale_factor; + + CreateFramebuffer(); +} + +void ImageTransportSurfaceFBO::SetLatencyInfo( + const std::vector<ui::LatencyInfo>& latency_info) { + for (size_t i = 0; i < latency_info.size(); i++) + latency_info_.push_back(latency_info[i]); +} + +void ImageTransportSurfaceFBO::WakeUpGpu() { + NOTIMPLEMENTED(); +} + +void ImageTransportSurfaceFBO::OnWillDestroyStub() { + helper_->stub()->RemoveDestructionObserver(this); + Destroy(); +} + +void ImageTransportSurfaceFBO::DestroyFramebuffer() { + // If we have resources to destroy, then make sure that we have a current + // context which we can use to delete the resources. + if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) { + DCHECK(gfx::GLContext::GetCurrent() == context_); + DCHECK(context_->IsCurrent(this)); + DCHECK(CGLGetCurrentContext()); + } + + if (fbo_id_) { + glDeleteFramebuffersEXT(1, &fbo_id_); + fbo_id_ = 0; + } + + if (texture_id_) { + glDeleteTextures(1, &texture_id_); + texture_id_ = 0; + } + + if (depth_stencil_renderbuffer_id_) { + glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); + depth_stencil_renderbuffer_id_ = 0; + } + + storage_provider_->FreeColorBufferStorage(); + + has_complete_framebuffer_ = false; +} + +void ImageTransportSurfaceFBO::CreateFramebuffer() { + gfx::Size new_rounded_size = storage_provider_->GetRoundedSize(size_); + + // Only recreate surface when the rounded up size has changed. + if (has_complete_framebuffer_ && new_rounded_size == rounded_size_) + return; + + // This trace event is used in gpu_feature_browsertest.cc - the test will need + // to be updated if this event is changed or moved. + TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::CreateFramebuffer", + "width", new_rounded_size.width(), + "height", new_rounded_size.height()); + + rounded_size_ = new_rounded_size; + + // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on + // Mac OS X and is required for IOSurface interoperability. + GLint previous_texture_id = 0; + glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id); + + // Free the old IO Surface first to reduce memory fragmentation. + DestroyFramebuffer(); + + glGenFramebuffersEXT(1, &fbo_id_); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); + + glGenTextures(1, &texture_id_); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id_); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, + GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, + GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_RECTANGLE_ARB, + texture_id_, + 0); + + // Search through the provided attributes; if the caller has + // requested a stencil buffer, try to get one. + + int32 stencil_bits = + helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE); + if (stencil_bits > 0) { + // Create and bind the stencil buffer + bool has_packed_depth_stencil = + GLSurface::ExtensionsContain( + reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)), + "GL_EXT_packed_depth_stencil"); + + if (has_packed_depth_stencil) { + glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, + depth_stencil_renderbuffer_id_); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, + rounded_size_.width(), rounded_size_.height()); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + depth_stencil_renderbuffer_id_); + } + + // If we asked for stencil but the extension isn't present, + // it's OK to silently fail; subsequent code will/must check + // for the presence of a stencil buffer before attempting to + // do stencil-based operations. + } + + bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage( + static_cast<CGLContextObj>(context_->GetHandle()), texture_id_, + rounded_size_, scale_factor_); + if (!allocated_color_buffer) { + DLOG(ERROR) << "Failed to allocate color buffer storage."; + DestroyFramebuffer(); + return; + } + + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + DLOG(ERROR) << "Framebuffer was incomplete: " << status; + DestroyFramebuffer(); + return; + } + + has_complete_framebuffer_ = true; + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, previous_texture_id); + // The FBO remains bound for this GL context. +} + +} // namespace content |