| // Copyright 2013 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/browser/renderer_host/software_frame_manager.h" |
| |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/debug/alias.h" |
| #include "content/browser/renderer_host/dip_util.h" |
| #include "content/public/browser/user_metrics.h" |
| |
| namespace { |
| |
| void ReleaseMailbox(scoped_refptr<content::SoftwareFrame> frame, |
| unsigned sync_point, |
| bool lost_resource) {} |
| |
| } // namespace |
| |
| namespace content { |
| |
| // Keep a count of how many memory buffers are in existence. |
| // TODO(jbauman): remove once we have enough info. |
| static int g_outstanding_buffers; |
| |
| #if defined(OS_WIN) |
| unsigned int g_crash_count; |
| |
| // Capture a stack dump. |
| // TODO(jbauman): Remove this terrible layering violation once we have some |
| // dumps. http://crbug.com/311792 |
| void DumpWithoutCrashing() { |
| if (++g_crash_count > 2) |
| return; |
| int saved_buffers = g_outstanding_buffers; |
| base::debug::Alias(static_cast<void*>(&saved_buffers)); |
| // Get the breakpad pointer from chrome.exe |
| typedef void(__cdecl * DumpProcessFunction)(); |
| DumpProcessFunction DumpProcess = |
| reinterpret_cast<DumpProcessFunction>(::GetProcAddress( |
| ::GetModuleHandle(L"chrome.exe"), "DumpProcessWithoutCrash")); |
| if (DumpProcess) |
| DumpProcess(); |
| } |
| #endif |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // SoftwareFrame |
| |
| class CONTENT_EXPORT SoftwareFrame : public base::RefCounted<SoftwareFrame> { |
| private: |
| friend class base::RefCounted<SoftwareFrame>; |
| friend class SoftwareFrameManager; |
| |
| SoftwareFrame( |
| base::WeakPtr<SoftwareFrameManagerClient> frame_manager_client, |
| uint32 output_surface_id, |
| unsigned frame_id, |
| gfx::Size frame_size_dip, |
| gfx::Size frame_size_pixels, |
| scoped_ptr<base::SharedMemory> shared_memory); |
| ~SoftwareFrame(); |
| |
| base::WeakPtr<SoftwareFrameManagerClient> frame_manager_client_; |
| const uint32 output_surface_id_; |
| const unsigned frame_id_; |
| const gfx::Size frame_size_dip_; |
| const gfx::Size frame_size_pixels_; |
| scoped_ptr<base::SharedMemory> shared_memory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(SoftwareFrame); |
| }; |
| |
| SoftwareFrame::SoftwareFrame( |
| base::WeakPtr<SoftwareFrameManagerClient> frame_manager_client, |
| uint32 output_surface_id, |
| unsigned frame_id, |
| gfx::Size frame_size_dip, |
| gfx::Size frame_size_pixels, |
| scoped_ptr<base::SharedMemory> shared_memory) |
| : frame_manager_client_(frame_manager_client), |
| output_surface_id_(output_surface_id), |
| frame_id_(frame_id), |
| frame_size_dip_(frame_size_dip), |
| frame_size_pixels_(frame_size_pixels), |
| shared_memory_(shared_memory.Pass()) { |
| ++g_outstanding_buffers; |
| } |
| |
| SoftwareFrame::~SoftwareFrame() { |
| --g_outstanding_buffers; |
| if (frame_manager_client_) { |
| frame_manager_client_->SoftwareFrameWasFreed( |
| output_surface_id_, frame_id_); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // SoftwareFrameManager |
| |
| SoftwareFrameManager::SoftwareFrameManager( |
| base::WeakPtr<SoftwareFrameManagerClient> client) |
| : client_(client) {} |
| |
| SoftwareFrameManager::~SoftwareFrameManager() { |
| DiscardCurrentFrame(); |
| } |
| |
| bool SoftwareFrameManager::SwapToNewFrame( |
| uint32 output_surface_id, |
| const cc::SoftwareFrameData* frame_data, |
| float frame_device_scale_factor, |
| base::ProcessHandle process_handle) { |
| |
| #ifdef OS_WIN |
| scoped_ptr<base::SharedMemory> shared_memory( |
| new base::SharedMemory(frame_data->handle, true, |
| process_handle)); |
| #else |
| scoped_ptr<base::SharedMemory> shared_memory( |
| new base::SharedMemory(frame_data->handle, true)); |
| #endif |
| |
| // The NULL handle is used in testing. |
| if (base::SharedMemory::IsHandleValid(shared_memory->handle())) { |
| const size_t size_in_bytes = 4 * frame_data->size.GetArea(); |
| #ifdef OS_WIN |
| if (!shared_memory->Map(0)) { |
| DumpWithoutCrashing(); |
| DLOG(ERROR) << "Unable to map renderer memory."; |
| RecordAction( |
| UserMetricsAction("BadMessageTerminate_SharedMemoryManager1")); |
| return false; |
| } |
| |
| if (shared_memory->mapped_size() < size_in_bytes) { |
| DumpWithoutCrashing(); |
| DLOG(ERROR) << "Shared memory too small for given rectangle"; |
| RecordAction( |
| UserMetricsAction("BadMessageTerminate_SharedMemoryManager2")); |
| return false; |
| } |
| #else |
| if (!shared_memory->Map(size_in_bytes)) { |
| DLOG(ERROR) << "Unable to map renderer memory."; |
| RecordAction( |
| UserMetricsAction("BadMessageTerminate_SharedMemoryManager1")); |
| return false; |
| } |
| #endif |
| } |
| |
| scoped_refptr<SoftwareFrame> next_frame(new SoftwareFrame( |
| client_, |
| output_surface_id, |
| frame_data->id, |
| ConvertSizeToDIP(frame_device_scale_factor, frame_data->size), |
| frame_data->size, |
| shared_memory.Pass())); |
| current_frame_.swap(next_frame); |
| return true; |
| } |
| |
| bool SoftwareFrameManager::HasCurrentFrame() const { |
| return current_frame_.get() ? true : false; |
| } |
| |
| void SoftwareFrameManager::DiscardCurrentFrame() { |
| if (!HasCurrentFrame()) |
| return; |
| current_frame_ = NULL; |
| RendererFrameManager::GetInstance()->RemoveFrame(this); |
| } |
| |
| void SoftwareFrameManager::SwapToNewFrameComplete(bool visible) { |
| DCHECK(HasCurrentFrame()); |
| RendererFrameManager::GetInstance()->AddFrame(this, visible); |
| } |
| |
| void SoftwareFrameManager::SetVisibility(bool visible) { |
| if (HasCurrentFrame()) { |
| RendererFrameManager::GetInstance()->SetFrameVisibility(this, visible); |
| } |
| } |
| |
| void SoftwareFrameManager::GetCurrentFrameMailbox( |
| cc::TextureMailbox* mailbox, |
| scoped_ptr<cc::SingleReleaseCallback>* callback) { |
| DCHECK(HasCurrentFrame()); |
| *mailbox = cc::TextureMailbox( |
| current_frame_->shared_memory_.get(), current_frame_->frame_size_pixels_); |
| *callback = cc::SingleReleaseCallback::Create( |
| base::Bind(ReleaseMailbox, current_frame_)); |
| } |
| |
| const void* SoftwareFrameManager::GetCurrentFramePixels() const { |
| DCHECK(HasCurrentFrame()); |
| DCHECK(base::SharedMemory::IsHandleValid( |
| current_frame_->shared_memory_->handle())); |
| return current_frame_->shared_memory_->memory(); |
| } |
| |
| gfx::Size SoftwareFrameManager::GetCurrentFrameSizeInPixels() const { |
| DCHECK(HasCurrentFrame()); |
| return current_frame_->frame_size_pixels_; |
| } |
| |
| gfx::Size SoftwareFrameManager::GetCurrentFrameSizeInDIP() const { |
| DCHECK(HasCurrentFrame()); |
| return current_frame_->frame_size_dip_; |
| } |
| |
| void SoftwareFrameManager::EvictCurrentFrame() { |
| DCHECK(HasCurrentFrame()); |
| DiscardCurrentFrame(); |
| if (client_) |
| client_->ReleaseReferencesToSoftwareFrame(); |
| } |
| |
| } // namespace content |