diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-20 02:00:04 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-20 02:00:04 +0000 |
commit | e68e62fa169c45bd779bfe890aa4fcdaa24d267d (patch) | |
tree | efdb18adec880e7f780d8cde4e12893d3a20234f /chrome/renderer/render_process.cc | |
parent | 7fe07d0726bad485fa40150aa9f7cecb1318217e (diff) | |
download | chromium_src-e68e62fa169c45bd779bfe890aa4fcdaa24d267d.zip chromium_src-e68e62fa169c45bd779bfe890aa4fcdaa24d267d.tar.gz chromium_src-e68e62fa169c45bd779bfe890aa4fcdaa24d267d.tar.bz2 |
Bitmap transport
This patch reworks bitmap transport on all platforms. Linux and Mac
are switched from serialising bitmaps over the IPC channel to using
shared memory. All platforms gain a shared memory mapping cache on the
host side.
The concept of a TransportDIB (device independent bitmap) is added to
encapsulate most of the platform specifics.
On Linux, we use SysV shared memory. This is because X shared pixmaps,
which predate POSIX SHM, can only use SysV. By using SysV between
renderer and browser, we open up the possibility to map the shared
memory directly from the renderer to the X server.
On Mac, we use POSIX shared memory. However, since this needs
filesystem access and the Mac renderer is sandboxed from the
filesystem, we add two new messages from renderer -> browser:
The first, AllocTransportDIB, synchronously creates a transport DIB in
the browser and passes a handle back to the renderer. The second,
FreeTransportDIB, asynchronously, notifies the browser that it may
close its handle to the shared memory region.
On Mac, the shared memory regions are identified by their inode
numbers on the wire. This means that the browser must keep handles
open to all the allocated shared memory regions (since an inode number
is insufficient to map the region). The alternative design is that the
renderer passes the file descriptor with each paint operation. Since
passing file descriptors is special case in the code, I felt that it
would be best to minimise their use. Creating and freeing transport
DIBs are relatively rare operations relative to paints and scrolls.
On Windows, most of the code remains the same, except that Windows now
uses the mapping cache added in this patch. This allows the browser to
maintain a shared memory mapping for a transport DIB over several
paints. Previously it mapped and unmapped for every operation, causing
lots of TLB and VM churn.
Review URL: http://codereview.chromium.org/21485
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10071 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/renderer/render_process.cc')
-rw-r--r-- | chrome/renderer/render_process.cc | 163 |
1 files changed, 107 insertions, 56 deletions
diff --git a/chrome/renderer/render_process.cc b/chrome/renderer/render_process.cc index 853693b..198f854f 100644 --- a/chrome/renderer/render_process.cc +++ b/chrome/renderer/render_process.cc @@ -25,6 +25,7 @@ #include "chrome/common/ipc_channel.h" #include "chrome/common/ipc_message_utils.h" #include "chrome/common/render_messages.h" +#include "chrome/common/transport_dib.h" #include "chrome/renderer/render_view.h" #include "webkit/glue/webkit_glue.h" @@ -36,7 +37,10 @@ bool RenderProcess::load_plugins_in_process_ = false; RenderProcess::RenderProcess(const std::wstring& channel_name) : render_thread_(channel_name), - ALLOW_THIS_IN_INITIALIZER_LIST(clearer_factory_(this)) { + ALLOW_THIS_IN_INITIALIZER_LIST(shared_mem_cache_cleaner_( + base::TimeDelta::FromSeconds(5), + this, &RenderProcess::ClearTransportDIBCache)), + sequence_number_(0) { for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) shared_mem_cache_[i] = NULL; } @@ -48,7 +52,7 @@ RenderProcess::~RenderProcess() { // This race condition causes a crash when the renderer process is shutting // down. render_thread_.Stop(); - ClearSharedMemCache(); + ClearTransportDIBCache(); } // static @@ -124,97 +128,144 @@ bool RenderProcess::ShouldLoadPluginsInProcess() { return load_plugins_in_process_; } -// static -base::SharedMemory* RenderProcess::AllocSharedMemory(size_t size) { - self()->clearer_factory_.RevokeAll(); - - base::SharedMemory* mem = self()->GetSharedMemFromCache(size); - if (mem) - return mem; +// ----------------------------------------------------------------------------- +// Platform specific code for dealing with bitmap transport... - // Round-up size to allocation granularity - size_t allocation_granularity = base::SysInfo::VMAllocationGranularity(); - size = size / allocation_granularity + 1; - size = size * allocation_granularity; +// ----------------------------------------------------------------------------- +// Create a platform canvas object which renders into the given transport +// memory. +// ----------------------------------------------------------------------------- +static skia::PlatformCanvas* CanvasFromTransportDIB( + TransportDIB* dib, const gfx::Rect& rect) { +#if defined(OS_WIN) + return new skia::PlatformCanvas(rect.width(), rect.height(), true, + dib->handle()); +#elif defined(OS_LINUX) || defined(OS_MACOSX) + return new skia::PlatformCanvas(rect.width(), rect.height(), true, + reinterpret_cast<uint8_t*>(dib->memory())); +#endif +} - mem = new base::SharedMemory(); - if (!mem) +TransportDIB* RenderProcess::CreateTransportDIB(size_t size) { +#if defined(OS_WIN) || defined(OS_LINUX) + // Windows and Linux create transport DIBs inside the renderer + return TransportDIB::Create(size, sequence_number_++); +#elif defined(OS_MACOSX) // defined(OS_WIN) || defined(OS_LINUX) + // Mac creates transport DIBs in the browser, so we need to do a sync IPC to + // get one. + IPC::Maybe<TransportDIB::Handle> mhandle; + IPC::Message* msg = new ViewHostMsg_AllocTransportDIB(size, &mhandle); + if (!render_thread_.Send(msg)) return NULL; - if (!mem->Create(L"", false, true, size)) { - delete mem; + if (!mhandle.valid) return NULL; + return TransportDIB::Map(mhandle.value); +#endif // defined(OS_MACOSX) +} + +void RenderProcess::FreeTransportDIB(TransportDIB* dib) { + if (!dib) + return; + +#if defined(OS_MACOSX) + // On Mac we need to tell the browser that it can drop a reference to the + // shared memory. + IPC::Message* msg = new ViewHostMsg_FreeTransportDIB(dib->id()); + render_thread_.Send(msg); +#endif + + delete dib; +} + +// ----------------------------------------------------------------------------- + + +// static +skia::PlatformCanvas* RenderProcess::GetDrawingCanvas( + TransportDIB** memory, const gfx::Rect& rect) { + const size_t stride = skia::PlatformCanvas::StrideForWidth(rect.width()); + const size_t size = stride * rect.height(); + + if (!self()->GetTransportDIBFromCache(memory, size)) { + *memory = self()->CreateTransportDIB(size); + if (!*memory) + return false; } - return mem; + return CanvasFromTransportDIB(*memory, rect); } // static -void RenderProcess::FreeSharedMemory(base::SharedMemory* mem) { +void RenderProcess::ReleaseTransportDIB(TransportDIB* mem) { if (self()->PutSharedMemInCache(mem)) { - self()->ScheduleCacheClearer(); + self()->shared_mem_cache_cleaner_.Reset(); return; } - DeleteSharedMem(mem); -} -// static -void RenderProcess::DeleteSharedMem(base::SharedMemory* mem) { - delete mem; + self()->FreeTransportDIB(mem); } -base::SharedMemory* RenderProcess::GetSharedMemFromCache(size_t size) { +bool RenderProcess::GetTransportDIBFromCache(TransportDIB** mem, + size_t size) { // look for a cached object that is suitable for the requested size. for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) { - base::SharedMemory* mem = shared_mem_cache_[i]; - if (mem && mem->max_size() >= size) { + if (shared_mem_cache_[i] && + size <= shared_mem_cache_[i]->size()) { + *mem = shared_mem_cache_[i]; shared_mem_cache_[i] = NULL; - return mem; + return true; } } - return NULL; + + return false; } -bool RenderProcess::PutSharedMemInCache(base::SharedMemory* mem) { +int RenderProcess::FindFreeCacheSlot(size_t size) { // simple algorithm: // - look for an empty slot to store mem, or - // - if full, then replace any existing cache entry that is smaller than the - // given shared memory object. + // - if full, then replace smallest entry which is smaller than |size| for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) { - if (!shared_mem_cache_[i]) { - shared_mem_cache_[i] = mem; - return true; - } + if (shared_mem_cache_[i] == NULL) + return i; } - for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) { - base::SharedMemory* cached_mem = shared_mem_cache_[i]; - if (cached_mem->max_size() < mem->max_size()) { - shared_mem_cache_[i] = mem; - DeleteSharedMem(cached_mem); - return true; + + size_t smallest_size = size; + int smallest_index = -1; + + for (size_t i = 1; i < arraysize(shared_mem_cache_); ++i) { + const size_t entry_size = shared_mem_cache_[i]->size(); + if (entry_size < smallest_size) { + smallest_size = entry_size; + smallest_index = i; } } - return false; + + if (smallest_index != -1) { + FreeTransportDIB(shared_mem_cache_[smallest_index]); + shared_mem_cache_[smallest_index] = NULL; + } + + return smallest_index; } -void RenderProcess::ClearSharedMemCache() { +bool RenderProcess::PutSharedMemInCache(TransportDIB* mem) { + const int slot = FindFreeCacheSlot(mem->size()); + if (slot == -1) + return false; + + shared_mem_cache_[slot] = mem; + return true; +} + +void RenderProcess::ClearTransportDIBCache() { for (size_t i = 0; i < arraysize(shared_mem_cache_); ++i) { if (shared_mem_cache_[i]) { - DeleteSharedMem(shared_mem_cache_[i]); + FreeTransportDIB(shared_mem_cache_[i]); shared_mem_cache_[i] = NULL; } } } -void RenderProcess::ScheduleCacheClearer() { - // If we already have a deferred clearer, then revoke it so we effectively - // delay cache clearing until idle for our desired interval. - clearer_factory_.RevokeAll(); - - MessageLoop::current()->PostDelayedTask(FROM_HERE, - clearer_factory_.NewRunnableMethod(&RenderProcess::ClearSharedMemCache), - 5000 /* 5 seconds */); -} - void RenderProcess::Cleanup() { // TODO(port) // Try and limit what we pull in for our non-Win unit test bundle |