diff options
author | apatrick@chromium.org <apatrick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-25 22:08:35 +0000 |
---|---|---|
committer | apatrick@chromium.org <apatrick@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-25 22:08:35 +0000 |
commit | 6217d397bf501ec1ec5b7270d493006badd4d87a (patch) | |
tree | fce6c8e9c15b79bab29a9535ce9929dd9d53735b | |
parent | 21dedcc12d21ccb288244249522d77bc074c501e (diff) | |
download | chromium_src-6217d397bf501ec1ec5b7270d493006badd4d87a.zip chromium_src-6217d397bf501ec1ec5b7270d493006badd4d87a.tar.gz chromium_src-6217d397bf501ec1ec5b7270d493006badd4d87a.tar.bz2 |
Calling OpenGL from the renderer process
- Added ability for renderer processes to render to a real window (Windows only so far).
- Added ability to create offscreen frame buffer objects that can be resized later.
- OpenGL context can have a "parent" context that can access its last swapped back buffer through a texture ID.
- Moved code to establish GPU channel from RenderWidget to RenderThread.
- Changed way service size command buffer object lifetimes are managed.
TEST=trybot and visual verification that OpenGL can clear the browser window to magenta.
BUG=none
Review URL: http://codereview.chromium.org/1136006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@42679 0039d316-1c4b-4281-b951-d872f2087c98
49 files changed, 1251 insertions, 311 deletions
diff --git a/chrome/browser/gpu_process_host.cc b/chrome/browser/gpu_process_host.cc index fc10107..9fa0092 100644 --- a/chrome/browser/gpu_process_host.cc +++ b/chrome/browser/gpu_process_host.cc @@ -126,13 +126,11 @@ void GpuProcessHost::RemoveRoute(int32 routing_id) { router_.RemoveRoute(routing_id); } -void GpuProcessHost::EstablishGpuChannel( - int renderer_id, - int routing_id) { +void GpuProcessHost::EstablishGpuChannel(int renderer_id) { if (Send(new GpuMsg_EstablishChannel(renderer_id))) - sent_requests_.push(ChannelRequest(renderer_id, routing_id)); + sent_requests_.push(ChannelRequest(renderer_id)); else - ReplyToRenderer(renderer_id, routing_id, IPC::ChannelHandle()); + ReplyToRenderer(renderer_id, IPC::ChannelHandle()); } void GpuProcessHost::OnControlMessageReceived(const IPC::Message& message) { @@ -146,21 +144,19 @@ void GpuProcessHost::OnChannelEstablished( const IPC::ChannelHandle& channel_handle) { const ChannelRequest& request = sent_requests_.front(); - ReplyToRenderer(request.renderer_id, request.routing_id, channel_handle); + ReplyToRenderer(request.renderer_id, channel_handle); sent_requests_.pop(); } void GpuProcessHost::ReplyToRenderer( int renderer_id, - int routing_id, const IPC::ChannelHandle& channel) { // Check whether the renderer process is still around. RenderProcessHost* process_host = RenderProcessHost::FromID(renderer_id); if (!process_host) return; - CHECK(process_host->Send(new ViewMsg_GpuChannelEstablished(routing_id, - channel))); + CHECK(process_host->Send(new ViewMsg_GpuChannelEstablished(channel))); } void GpuProcessHost::PropagateBrowserCommandLineToGpu( diff --git a/chrome/browser/gpu_process_host.h b/chrome/browser/gpu_process_host.h index 36d068f..4d74e00 100644 --- a/chrome/browser/gpu_process_host.h +++ b/chrome/browser/gpu_process_host.h @@ -13,6 +13,7 @@ #include "chrome/browser/child_process_launcher.h" #include "chrome/common/gpu_native_window_handle.h" #include "chrome/common/message_router.h" +#include "gfx/native_widget_types.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_channel_proxy.h" @@ -50,26 +51,20 @@ class GpuProcessHost : public IPC::Channel::Sender, // Tells the GPU process to create a new channel for communication with a // renderer. Will asynchronously send message to object with given routing id // on completion. - void EstablishGpuChannel(int renderer_id, int routing_id); + void EstablishGpuChannel(int renderer_id); private: friend struct DefaultSingletonTraits<GpuProcessHost>; // Used to queue pending channel requests. struct ChannelRequest { - ChannelRequest(int renderer_id, - int routing_id) : - renderer_id(renderer_id), - routing_id(routing_id) {} + explicit ChannelRequest(int renderer_id) : renderer_id(renderer_id) {} // Used to identify the renderer. The ID is used instead of a pointer to // the RenderProcessHost in case it is destroyed while the request is // pending. // TODO(apatrick): investigate whether these IDs are used for future // render processes. int renderer_id; - - // Routing ID of object to receive reply message. - int routing_id; }; GpuProcessHost(); @@ -81,7 +76,6 @@ class GpuProcessHost : public IPC::Channel::Sender, void OnChannelEstablished(const IPC::ChannelHandle& channel_handle); void ReplyToRenderer(int renderer_id, - int routing_id, const IPC::ChannelHandle& channel); // These are the channel requests that we have already sent to diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index 0757d17..dccc34d 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -29,6 +29,7 @@ #include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/extensions/user_script_master.h" +#include "chrome/browser/gpu_process_host.h" #include "chrome/browser/history/history.h" #include "chrome/browser/io_thread.h" #include "chrome/browser/net/url_request_context_getter.h" @@ -45,6 +46,7 @@ #include "chrome/browser/visitedlink_master.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/child_process_info.h" +#include "chrome/common/gpu_messages.h" #include "chrome/common/logging_chrome.h" #include "chrome/common/notification_service.h" #include "chrome/common/pref_names.h" @@ -770,6 +772,8 @@ void BrowserRenderProcessHost::OnMessageReceived(const IPC::Message& msg) { OnExtensionRemoveListener) IPC_MESSAGE_HANDLER(ViewHostMsg_ExtensionCloseChannel, OnExtensionCloseChannel) + IPC_MESSAGE_HANDLER(ViewHostMsg_EstablishGpuChannel, + OnMsgEstablishGpuChannel) IPC_MESSAGE_HANDLER(ViewHostMsg_SpellChecker_RequestDictionary, OnSpellCheckerRequestDictionary) IPC_MESSAGE_UNHANDLED_ERROR() @@ -974,6 +978,10 @@ void BrowserRenderProcessHost::OnExtensionCloseChannel(int port_id) { } } +void BrowserRenderProcessHost::OnMsgEstablishGpuChannel() { + GpuProcessHost::Get()->EstablishGpuChannel(id()); +} + void BrowserRenderProcessHost::OnSpellCheckerRequestDictionary() { if (profile()->GetSpellCheckHost()) { // The spellchecker initialization already started and finished; just send diff --git a/chrome/browser/renderer_host/browser_render_process_host.h b/chrome/browser/renderer_host/browser_render_process_host.h index 277a53e..372045a 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.h +++ b/chrome/browser/renderer_host/browser_render_process_host.h @@ -110,6 +110,7 @@ class BrowserRenderProcessHost : public RenderProcessHost, void OnExtensionAddListener(const std::string& event_name); void OnExtensionRemoveListener(const std::string& event_name); void OnExtensionCloseChannel(int port_id); + void OnMsgEstablishGpuChannel(); // Initialize support for visited links. Send the renderer process its initial // set of visited links. diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc index 152aae1..5dc9badc 100644 --- a/chrome/browser/renderer_host/render_widget_host.cc +++ b/chrome/browser/renderer_host/render_widget_host.cc @@ -8,7 +8,6 @@ #include "base/histogram.h" #include "base/keyboard_codes.h" #include "base/message_loop.h" -#include "chrome/browser/gpu_process_host.h" #include "chrome/browser/renderer_host/backing_store.h" #include "chrome/browser/renderer_host/backing_store_manager.h" #include "chrome/browser/renderer_host/render_process_host.h" @@ -16,7 +15,6 @@ #include "chrome/browser/renderer_host/render_widget_host_painting_observer.h" #include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/browser/renderer_host/video_layer.h" -#include "chrome/common/gpu_messages.h" #include "chrome/common/notification_service.h" #include "chrome/common/render_messages.h" #include "webkit/glue/webcursor.h" @@ -139,8 +137,6 @@ void RenderWidgetHost::OnMessageReceived(const IPC::Message &msg) { IPC_MESSAGE_HANDLER(ViewHostMsg_FocusedNodeChanged, OnMsgFocusedNodeChanged) IPC_MESSAGE_HANDLER(ViewHostMsg_SetCursor, OnMsgSetCursor) IPC_MESSAGE_HANDLER(ViewHostMsg_ImeUpdateStatus, OnMsgImeUpdateStatus) - IPC_MESSAGE_HANDLER(ViewHostMsg_EstablishGpuChannel, - OnMsgEstablishGpuChannel) #if defined(OS_LINUX) IPC_MESSAGE_HANDLER(ViewHostMsg_CreatePluginContainer, OnMsgCreatePluginContainer) @@ -869,10 +865,6 @@ void RenderWidgetHost::OnMsgImeUpdateStatus(int control, } } -void RenderWidgetHost::OnMsgEstablishGpuChannel() { - GpuProcessHost::Get()->EstablishGpuChannel(process_->id(), routing_id_); -} - #if defined(OS_LINUX) void RenderWidgetHost::OnMsgCreatePluginContainer(gfx::PluginWindowHandle id) { diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h index 5c95535..f8b8218 100644 --- a/chrome/browser/renderer_host/render_widget_host.h +++ b/chrome/browser/renderer_host/render_widget_host.h @@ -445,10 +445,6 @@ class RenderWidgetHost : public IPC::Channel::Listener, // having to bring in render_messages.h in a header file. void OnMsgImeUpdateStatus(int control, const gfx::Rect& caret_rect); - // Renderer process is requesting that the browser process establish a GPU - // channel. - void OnMsgEstablishGpuChannel(); - #if defined(OS_LINUX) void OnMsgCreatePluginContainer(gfx::PluginWindowHandle id); void OnMsgDestroyPluginContainer(gfx::PluginWindowHandle id); diff --git a/chrome/browser/renderer_host/render_widget_host_view_win.cc b/chrome/browser/renderer_host/render_widget_host_view_win.cc index 55e2762..2d08f42 100644 --- a/chrome/browser/renderer_host/render_widget_host_view_win.cc +++ b/chrome/browser/renderer_host/render_widget_host_view_win.cc @@ -293,6 +293,15 @@ RenderWidgetHostViewWin::~RenderWidgetHostViewWin() { void RenderWidgetHostViewWin::CreateWnd(HWND parent) { Create(parent); // ATL function to create the window. + + // Add a property indicating that a particular renderer is associated with + // this window. Used by the GPU process to validate window handles it + // receives from renderer processes. + int renderer_id = render_widget_host_->process()->id(); + SetProp(m_hWnd, + chrome::kChromiumRendererIdProperty, + reinterpret_cast<HANDLE>(renderer_id)); + // Uncommenting this will enable experimental out-of-process painting. // Contact brettw for more, // gpu_view_host_.reset(new GpuViewHost(render_widget_host_, m_hWnd)); diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc index 38cf59e..a6ad651 100644 --- a/chrome/common/chrome_constants.cc +++ b/chrome/common/chrome_constants.cc @@ -134,6 +134,8 @@ const int kHistogramSynchronizerReservedSequenceNumber = 0; const int kMaxSessionHistoryEntries = 50; +const wchar_t kChromiumRendererIdProperty[] = L"ChromiumRendererId"; + } // namespace chrome #undef FPL diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h index 554a48b..456cea0 100644 --- a/chrome/common/chrome_constants.h +++ b/chrome/common/chrome_constants.h @@ -83,6 +83,8 @@ extern const int kHistogramSynchronizerReservedSequenceNumber; // The maximum number of session history entries per tab. extern const int kMaxSessionHistoryEntries; +extern const wchar_t kChromiumRendererIdProperty[]; + } // namespace chrome #endif // CHROME_COMMON_CHROME_CONSTANTS_H_ diff --git a/chrome/common/gpu_messages_internal.h b/chrome/common/gpu_messages_internal.h index 6780a5e..9107aed 100644 --- a/chrome/common/gpu_messages_internal.h +++ b/chrome/common/gpu_messages_internal.h @@ -9,6 +9,7 @@ // This file needs to be included again, even though we're actually included // from it via utility_messages.h. #include "base/shared_memory.h" +#include "gfx/size.h" #include "ipc/ipc_channel_handle.h" #include "ipc/ipc_message_macros.h" @@ -96,9 +97,21 @@ IPC_END_MESSAGES(GpuHost) // These are messages from a renderer process to the GPU process. IPC_BEGIN_MESSAGES(GpuChannel) - // Tells the GPU process to create a new command buffer with the given - // id. A corresponding GpuCommandBufferStub is created. - IPC_SYNC_MESSAGE_CONTROL0_1(GpuChannelMsg_CreateCommandBuffer, + // Tells the GPU process to create a new command buffer that renders directly + // to a native view. A corresponding GpuCommandBufferStub is created. + IPC_SYNC_MESSAGE_CONTROL1_1(GpuChannelMsg_CreateViewCommandBuffer, + gfx::NativeViewId, /* view */ + int32 /* route_id */) + + // Tells the GPU process to create a new command buffer that renders to an + // offscreen frame buffer. If parent_route_id is not zero, the texture backing + // the frame buffer is mapped into the corresponding parent command buffer's + // namespace, with the name of parent_texture_id. This ID is in the parent's + // namespace. + IPC_SYNC_MESSAGE_CONTROL3_1(GpuChannelMsg_CreateOffscreenCommandBuffer, + int32, /* parent_route_id */ + gfx::Size, /* size */ + uint32, /* parent_texture_id */ int32 /* route_id */) // The CommandBufferProxy sends this to the GpuCommandBufferStub in its @@ -168,6 +181,10 @@ IPC_BEGIN_MESSAGES(GpuCommandBuffer) // repainted. IPC_MESSAGE_ROUTED0(GpuCommandBufferMsg_NotifyRepaint) + // Tells the GPU process to resize an offscreen frame buffer. + IPC_MESSAGE_ROUTED1(GpuCommandBufferMsg_ResizeOffscreenFrameBuffer, + gfx::Size /* size */) + #if defined(OS_MACOSX) // On Mac OS X the GPU plugin must be offscreen, because there is no // true cross-process window hierarchy. For this reason we must send diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index a0f5d14..0372a49 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -604,8 +604,8 @@ IPC_BEGIN_MESSAGES(View) // The browser sends this to a renderer process in response to a // ViewHostMsg_EstablishGpuChannel message. - IPC_MESSAGE_ROUTED1(ViewMsg_GpuChannelEstablished, - IPC::ChannelHandle /* handle to channel */) + IPC_MESSAGE_CONTROL1(ViewMsg_GpuChannelEstablished, + IPC::ChannelHandle /* handle to channel */) // Notifies the renderer of the appcache that has been selected for a // a particular host. This is sent in reply to AppCacheMsg_SelectCache. @@ -1340,7 +1340,7 @@ IPC_BEGIN_MESSAGES(ViewHost) // create connect to the GPU. The browser will create the GPU process if // necessary, and will return a handle to the channel via // a GpuChannelEstablished message. - IPC_MESSAGE_ROUTED0(ViewHostMsg_EstablishGpuChannel) + IPC_MESSAGE_CONTROL0(ViewHostMsg_EstablishGpuChannel) // A renderer sends this to the browser process when it wants to start // a new instance of the Native Client process. The browser will launch diff --git a/chrome/gpu/gpu_channel.cc b/chrome/gpu/gpu_channel.cc index 4269022..b2b5bf9 100644 --- a/chrome/gpu/gpu_channel.cc +++ b/chrome/gpu/gpu_channel.cc @@ -2,6 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#if defined(OS_WIN) +#include <windows.h> +#endif + #include "chrome/gpu/gpu_channel.h" #include "base/command_line.h" @@ -11,6 +15,7 @@ #include "base/waitable_event.h" #include "build/build_config.h" #include "chrome/common/child_process.h" +#include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/gpu_messages.h" #include "chrome/gpu/gpu_thread.h" @@ -27,44 +32,12 @@ class GpuReleaseTask : public Task { } }; -typedef base::hash_map<std::string, scoped_refptr<GpuChannel> > - GpuChannelMap; - // How long we wait before releasing the GPU process. const int kGpuReleaseTimeMS = 10000; - -GpuChannelMap g_gpu_channels; } // namespace anonymous -GpuChannel* GpuChannel::EstablishGpuChannel(int renderer_id) { - // Map renderer ID to a (single) channel to that process. - std::string channel_name = StringPrintf( - "%d.r%d", base::GetCurrentProcId(), renderer_id); - - scoped_refptr<GpuChannel> channel; - - GpuChannelMap::const_iterator iter = g_gpu_channels.find(channel_name); - if (iter == g_gpu_channels.end()) { - channel = new GpuChannel; - } else { - channel = iter->second; - } - - DCHECK(channel != NULL); - - if (!channel->channel_.get()) { - if (channel->Init(channel_name)) { - g_gpu_channels[channel_name] = channel; - } else { - channel = NULL; - } - } - - return channel.get(); -} - -GpuChannel::GpuChannel() - : renderer_id_(-1) +GpuChannel::GpuChannel(int renderer_id) + : renderer_id_(renderer_id) #if defined(OS_POSIX) , renderer_fd_(-1) #endif @@ -141,8 +114,10 @@ bool GpuChannel::Send(IPC::Message* message) { void GpuChannel::OnControlMessageReceived(const IPC::Message& msg) { IPC_BEGIN_MESSAGE_MAP(GpuChannel, msg) - IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateCommandBuffer, - OnCreateCommandBuffer) + IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateViewCommandBuffer, + OnCreateViewCommandBuffer) + IPC_MESSAGE_HANDLER(GpuChannelMsg_CreateOffscreenCommandBuffer, + OnCreateOffscreenCommandBuffer) IPC_MESSAGE_HANDLER(GpuChannelMsg_DestroyCommandBuffer, OnDestroyCommandBuffer) IPC_MESSAGE_UNHANDLED_ERROR() @@ -154,11 +129,57 @@ int GpuChannel::GenerateRouteID() { return ++last_id; } -void GpuChannel::OnCreateCommandBuffer(int* route_id) { +void GpuChannel::OnCreateViewCommandBuffer(gfx::NativeViewId view_id, + int32* route_id) { + *route_id = 0; + #if defined(ENABLE_GPU) + +#if defined(OS_WIN) + gfx::NativeView view = gfx::NativeViewFromId(view_id); + + // Check that the calling renderer is allowed to render to this window. + // TODO(apatrick): consider killing the renderer process rather than failing. + int view_renderer_id = reinterpret_cast<int>( + GetProp(view, chrome::kChromiumRendererIdProperty)); + if (view_renderer_id != renderer_id_) + return; +#else + // TODO(apatrick): This needs to be something valid for mac and linux. + // Offscreen rendering will work on these platforms but not rendering to the + // window. + DCHECK_EQ(view_id, 0); + gfx::NativeView view = 0; +#endif + *route_id = GenerateRouteID(); scoped_refptr<GpuCommandBufferStub> stub = new GpuCommandBufferStub( - this, *route_id); + this, view, NULL, gfx::Size(), 0, *route_id); + router_.AddRoute(*route_id, stub); + stubs_[*route_id] = stub; +#endif // ENABLE_GPU +} + +void GpuChannel::OnCreateOffscreenCommandBuffer(int32 parent_route_id, + const gfx::Size& size, + uint32 parent_texture_id, + int32* route_id) { +#if defined(ENABLE_GPU) + *route_id = GenerateRouteID(); + scoped_refptr<GpuCommandBufferStub> parent_stub; + if (parent_route_id != 0) { + StubMap::iterator it = stubs_.find(parent_route_id); + DCHECK(it != stubs_.end()); + parent_stub = it->second; + } + + scoped_refptr<GpuCommandBufferStub> stub = new GpuCommandBufferStub( + this, + NULL, + parent_stub.get(), + size, + parent_texture_id, + *route_id); router_.AddRoute(*route_id, stub); stubs_[*route_id] = stub; #else @@ -166,7 +187,7 @@ void GpuChannel::OnCreateCommandBuffer(int* route_id) { #endif } -void GpuChannel::OnDestroyCommandBuffer(int route_id) { +void GpuChannel::OnDestroyCommandBuffer(int32 route_id) { #if defined(ENABLE_GPU) StubMap::iterator it = stubs_.find(route_id); DCHECK(it != stubs_.end()); @@ -175,8 +196,13 @@ void GpuChannel::OnDestroyCommandBuffer(int route_id) { #endif } -bool GpuChannel::Init(const std::string& channel_name) { - channel_name_ = channel_name; +bool GpuChannel::Init() { + // Check whether we're already initialized. + if (channel_.get()) + return true; + + // Map renderer ID to a (single) channel to that process. + std::string channel_name = GetChannelName(); #if defined(OS_POSIX) // This gets called when the GpuChannel is initially created. At this // point, create the socketpair and assign the GPU side FD to the channel @@ -192,3 +218,7 @@ bool GpuChannel::Init(const std::string& channel_name) { ChildProcess::current()->GetShutDownEvent())); return true; } + +std::string GpuChannel::GetChannelName() { + return StringPrintf("%d.r%d", base::GetCurrentProcId(), renderer_id_); +} diff --git a/chrome/gpu/gpu_channel.h b/chrome/gpu/gpu_channel.h index 17ea363..60dc906 100644 --- a/chrome/gpu/gpu_channel.h +++ b/chrome/gpu/gpu_channel.h @@ -13,6 +13,8 @@ #include "build/build_config.h" #include "chrome/common/message_router.h" #include "chrome/gpu/gpu_command_buffer_stub.h" +#include "gfx/native_widget_types.h" +#include "gfx/size.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_message.h" #include "ipc/ipc_sync_channel.h" @@ -23,17 +25,12 @@ class GpuChannel : public IPC::Channel::Listener, public IPC::Message::Sender, public base::RefCountedThreadSafe<GpuChannel> { public: - // Get a new GpuChannel object for the current process to talk to the - // given renderer process. The renderer ID is an opaque unique ID generated - // by the browser. - // - // POSIX only: If |channel_fd| > 0, use that file descriptor for the - // channel socket. - static GpuChannel* EstablishGpuChannel(int renderer_id); - + explicit GpuChannel(int renderer_id); virtual ~GpuChannel(); - std::string channel_name() const { return channel_name_; } + bool Init(); + + std::string GetChannelName(); base::ProcessHandle renderer_handle() const { return renderer_process_.handle(); @@ -60,21 +57,20 @@ class GpuChannel : public IPC::Channel::Listener, virtual bool Send(IPC::Message* msg); private: - // Called on the plugin thread - GpuChannel(); - - bool Init(const std::string& channel_name); - void OnControlMessageReceived(const IPC::Message& msg); int GenerateRouteID(); // Message handlers. - void OnCreateCommandBuffer(int* instance_id); - void OnDestroyCommandBuffer(int instance_id); + void OnCreateViewCommandBuffer(gfx::NativeViewId view, + int32* route_id); + void OnCreateOffscreenCommandBuffer(int32 parent_route_id, + const gfx::Size& size, + uint32 parent_texture_id, + int32* route_id); + void OnDestroyCommandBuffer(int32 route_id); scoped_ptr<IPC::SyncChannel> channel_; - std::string channel_name_; // Handle to the renderer process who is on the other side of the channel. base::ScopedOpenProcess renderer_process_; @@ -92,7 +88,7 @@ class GpuChannel : public IPC::Channel::Listener, MessageRouter router_; #if defined(ENABLE_GPU) - typedef base::hash_map<int, scoped_refptr<GpuCommandBufferStub> > StubMap; + typedef base::hash_map<int32, scoped_refptr<GpuCommandBufferStub> > StubMap; StubMap stubs_; #endif diff --git a/chrome/gpu/gpu_command_buffer_stub.cc b/chrome/gpu/gpu_command_buffer_stub.cc index 6c3f5cc..5d6842d 100644 --- a/chrome/gpu/gpu_command_buffer_stub.cc +++ b/chrome/gpu/gpu_command_buffer_stub.cc @@ -6,6 +6,7 @@ #include "base/process_util.h" #include "base/shared_memory.h" +#include "build/build_config.h" #include "chrome/common/gpu_messages.h" #include "chrome/gpu/gpu_channel.h" #include "chrome/gpu/gpu_command_buffer_stub.h" @@ -13,12 +14,23 @@ using gpu::Buffer; GpuCommandBufferStub::GpuCommandBufferStub(GpuChannel* channel, + gfx::NativeView view, + GpuCommandBufferStub* parent, + const gfx::Size& size, + uint32 parent_texture_id, int32 route_id) : channel_(channel), + view_(view), + parent_(parent), + initial_size_(size), + parent_texture_id_(parent_texture_id), route_id_(route_id) { } GpuCommandBufferStub::~GpuCommandBufferStub() { + if (processor_.get()) { + processor_->Destroy(); + } } void GpuCommandBufferStub::OnMessageReceived(const IPC::Message& message) { @@ -34,6 +46,8 @@ void GpuCommandBufferStub::OnMessageReceived(const IPC::Message& message) { OnDestroyTransferBuffer); IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_GetTransferBuffer, OnGetTransferBuffer); + IPC_MESSAGE_HANDLER(GpuCommandBufferMsg_ResizeOffscreenFrameBuffer, + OnResizeOffscreenFrameBuffer); IPC_MESSAGE_UNHANDLED_ERROR() IPC_END_MESSAGE_MAP() } @@ -55,8 +69,18 @@ void GpuCommandBufferStub::OnInitialize( if (command_buffer_->Initialize(size)) { Buffer buffer = command_buffer_->GetRingBuffer(); if (buffer.shared_memory) { - processor_ = new gpu::GPUProcessor(command_buffer_.get()); - if (processor_->Initialize(gfx::kNullPluginWindow)) { + gpu::GPUProcessor* parent_processor = + parent_ ? parent_->processor_.get() : NULL; + processor_.reset(new gpu::GPUProcessor(command_buffer_.get())); + // TODO(apatrick): The reinterpret_cast below is only valid on windows. +#if !defined(OS_WIN) + DCHECK_EQ(view_, static_cast<gfx::NativeView>(0)); +#endif + if (processor_->Initialize( + reinterpret_cast<gfx::PluginWindowHandle>(view_), + parent_processor, + initial_size_, + parent_texture_id_)) { command_buffer_->SetPutOffsetChangeCallback( NewCallback(processor_.get(), &gpu::GPUProcessor::ProcessCommands)); @@ -66,7 +90,7 @@ void GpuCommandBufferStub::OnInitialize( buffer.shared_memory->ShareToProcess(channel_->renderer_handle(), ring_buffer); } else { - processor_ = NULL; + processor_.reset(); command_buffer_.reset(); } } @@ -117,4 +141,8 @@ void GpuCommandBufferStub::OnGetTransferBuffer( } } +void GpuCommandBufferStub::OnResizeOffscreenFrameBuffer(const gfx::Size& size) { + processor_->ResizeOffscreenFrameBuffer(size); +} + #endif // ENABLE_GPU diff --git a/chrome/gpu/gpu_command_buffer_stub.h b/chrome/gpu/gpu_command_buffer_stub.h index eb927a3..7aaf68a 100644 --- a/chrome/gpu/gpu_command_buffer_stub.h +++ b/chrome/gpu/gpu_command_buffer_stub.h @@ -10,6 +10,7 @@ #include "base/process.h" #include "base/ref_counted.h" #include "gfx/native_widget_types.h" +#include "gfx/size.h" #include "gpu/command_buffer/service/command_buffer_service.h" #include "gpu/command_buffer/service/gpu_processor.h" #include "ipc/ipc_channel.h" @@ -23,6 +24,10 @@ class GpuCommandBufferStub public base::RefCountedThreadSafe<GpuCommandBufferStub> { public: GpuCommandBufferStub(GpuChannel* channel, + gfx::NativeView view, + GpuCommandBufferStub* parent, + const gfx::Size& size, + uint32 parent_texture_id, int32 route_id); virtual ~GpuCommandBufferStub(); @@ -33,7 +38,7 @@ class GpuCommandBufferStub // IPC::Message::Sender implementation: virtual bool Send(IPC::Message* msg); - int route_id() const { return route_id_; } + int32 route_id() const { return route_id_; } private: // Message handlers: @@ -47,12 +52,17 @@ class GpuCommandBufferStub void OnGetTransferBuffer(int32 id, base::SharedMemoryHandle* transfer_buffer, uint32* size); + void OnResizeOffscreenFrameBuffer(const gfx::Size& size); scoped_refptr<GpuChannel> channel_; - int route_id_; + gfx::NativeView view_; + scoped_refptr<GpuCommandBufferStub> parent_; + gfx::Size initial_size_; + uint32 parent_texture_id_; + int32 route_id_; scoped_ptr<gpu::CommandBufferService> command_buffer_; - scoped_refptr<gpu::GPUProcessor> processor_; + scoped_ptr<gpu::GPUProcessor> processor_; DISALLOW_COPY_AND_ASSIGN(GpuCommandBufferStub); }; diff --git a/chrome/gpu/gpu_thread.cc b/chrome/gpu/gpu_thread.cc index 1839412..9de441c 100644 --- a/chrome/gpu/gpu_thread.cc +++ b/chrome/gpu/gpu_thread.cc @@ -7,7 +7,6 @@ #include "build/build_config.h" #include "chrome/common/child_process.h" #include "chrome/common/gpu_messages.h" -#include "chrome/gpu/gpu_channel.h" #include "chrome/gpu/gpu_config.h" #if defined(OS_WIN) @@ -47,11 +46,28 @@ void GpuThread::OnControlMessageReceived(const IPC::Message& msg) { } void GpuThread::OnEstablishChannel(int renderer_id) { - scoped_refptr<GpuChannel> channel = - GpuChannel::EstablishGpuChannel(renderer_id); + scoped_refptr<GpuChannel> channel; + + GpuChannelMap::const_iterator iter = gpu_channels_.find(renderer_id); + if (iter == gpu_channels_.end()) { + channel = new GpuChannel(renderer_id); + } else { + channel = iter->second; + } + + DCHECK(channel != NULL); + + if (channel->Init()) { + // TODO(apatrick): figure out when to remove channels from the map. They + // will never be destroyed otherwise. + gpu_channels_[renderer_id] = channel; + } else { + channel = NULL; + } + IPC::ChannelHandle channel_handle; if (channel.get()) { - channel_handle.name = channel->channel_name(); + channel_handle.name = channel->GetChannelName(); #if defined(OS_POSIX) // On POSIX, pass the renderer-side FD. Also mark it as auto-close so that // it gets closed after it has been sent. diff --git a/chrome/gpu/gpu_thread.h b/chrome/gpu/gpu_thread.h index c32b513..23b8ba1 100644 --- a/chrome/gpu/gpu_thread.h +++ b/chrome/gpu/gpu_thread.h @@ -10,6 +10,7 @@ #include "build/build_config.h" #include "chrome/common/child_thread.h" #include "chrome/common/gpu_native_window_handle.h" +#include "chrome/gpu/gpu_channel.h" #include "chrome/gpu/gpu_config.h" #include "chrome/gpu/x_util.h" #include "gfx/native_widget_types.h" @@ -38,6 +39,9 @@ class GpuThread : public ChildThread { void OnNewRenderWidgetHostView(GpuNativeWindowHandle parent_window, int32 routing_id); + typedef base::hash_map<int, scoped_refptr<GpuChannel> > GpuChannelMap; + GpuChannelMap gpu_channels_; + #if defined(GPU_USE_GLX) Display* display_; scoped_ptr<GpuBackingStoreGLXContext> glx_context_; diff --git a/chrome/plugin/command_buffer_stub.cc b/chrome/plugin/command_buffer_stub.cc index 7dc049c..de18db6 100644 --- a/chrome/plugin/command_buffer_stub.cc +++ b/chrome/plugin/command_buffer_stub.cc @@ -92,8 +92,8 @@ void CommandBufferStub::OnInitialize(int32 size, } // Initialize the GPUProcessor. - processor_ = new gpu::GPUProcessor(command_buffer_.get()); - if (!processor_->Initialize(window_)) { + processor_.reset(new gpu::GPUProcessor(command_buffer_.get())); + if (!processor_->Initialize(window_, NULL, gfx::Size(), 0)) { Destroy(); return; } @@ -175,7 +175,7 @@ void CommandBufferStub::OnGetTransferBuffer( } void CommandBufferStub::Destroy() { - processor_ = NULL; + processor_.reset(); command_buffer_.reset(); DestroyPlatformSpecific(); diff --git a/chrome/plugin/command_buffer_stub.h b/chrome/plugin/command_buffer_stub.h index 026110f..83564ca 100644 --- a/chrome/plugin/command_buffer_stub.h +++ b/chrome/plugin/command_buffer_stub.h @@ -73,7 +73,7 @@ class CommandBufferStub : public IPC::Channel::Listener, gfx::PluginWindowHandle window_; int route_id_; scoped_ptr<gpu::CommandBufferService> command_buffer_; - scoped_refptr<gpu::GPUProcessor> processor_; + scoped_ptr<gpu::GPUProcessor> processor_; }; #endif // ENABLE_GPU diff --git a/chrome/renderer/command_buffer_proxy.cc b/chrome/renderer/command_buffer_proxy.cc index bd0f3a0..4a624a3 100644 --- a/chrome/renderer/command_buffer_proxy.cc +++ b/chrome/renderer/command_buffer_proxy.cc @@ -183,6 +183,10 @@ void CommandBufferProxy::SetParseError( NOTREACHED(); } +void CommandBufferProxy::ResizeOffscreenFrameBuffer(const gfx::Size& size) { + Send(new GpuCommandBufferMsg_ResizeOffscreenFrameBuffer(route_id_, size)); +} + #if defined(OS_MACOSX) void CommandBufferProxy::SetWindowSize(int32 width, int32 height) { Send(new GpuCommandBufferMsg_SetWindowSize(route_id_, width, height)); diff --git a/chrome/renderer/command_buffer_proxy.h b/chrome/renderer/command_buffer_proxy.h index 5d72f2c..e6fce0a 100644 --- a/chrome/renderer/command_buffer_proxy.h +++ b/chrome/renderer/command_buffer_proxy.h @@ -15,6 +15,7 @@ #include "base/scoped_ptr.h" #include "base/shared_memory.h" #include "base/task.h" +#include "gfx/size.h" #include "gpu/command_buffer/common/command_buffer.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_message.h" @@ -51,6 +52,9 @@ class CommandBufferProxy : public gpu::CommandBuffer, virtual void SetToken(int32 token); virtual void SetParseError(gpu::error::Error error); + // Asynchronously resizes an offscreen frame buffer. + void ResizeOffscreenFrameBuffer(const gfx::Size& size); + // Set a task that will be invoked the next time the window becomes invalid // and needs to be repainted. Takes ownership of task. void SetNotifyRepaintTask(Task* task) { diff --git a/chrome/renderer/ggl/ggl.cc b/chrome/renderer/ggl/ggl.cc index b5c1d9d..2c41126 100644 --- a/chrome/renderer/ggl/ggl.cc +++ b/chrome/renderer/ggl/ggl.cc @@ -50,12 +50,21 @@ class GLES2Initializer { // Manages a GL context. class Context { public: - Context(); + Context(GpuChannelHost* channel, Context* parent); ~Context(); // Initialize a GGL context that can be used in association with a a GPU // channel acquired from a RenderWidget or RenderView. - bool Initialize(GpuChannelHost* channel); + bool Initialize(gfx::NativeViewId view, const gfx::Size& size); + + // Asynchronously resizes an offscreen frame buffer. + void ResizeOffscreen(const gfx::Size& size); + + // For an offscreen frame buffer context, return the frame buffer ID with + // respect to the parent. + uint32 parent_texture_id() const { + return parent_texture_id_; + } // Destroy all resources associated with the GGL context. void Destroy(); @@ -73,6 +82,8 @@ class Context { private: scoped_refptr<GpuChannelHost> channel_; + Context* parent_; + uint32 parent_texture_id_; CommandBufferProxy* command_buffer_; gpu::gles2::GLES2CmdHelper* gles2_helper_; int32 transfer_buffer_id_; @@ -81,31 +92,46 @@ class Context { DISALLOW_COPY_AND_ASSIGN(Context); }; -Context::Context() - : channel_(NULL), +Context::Context(GpuChannelHost* channel, Context* parent) + : channel_(channel), + parent_(parent), + parent_texture_id_(0), command_buffer_(NULL), gles2_helper_(NULL), transfer_buffer_id_(0), gles2_implementation_(NULL) { + DCHECK(channel); } Context::~Context() { Destroy(); } -bool Context::Initialize(GpuChannelHost* channel) { - DCHECK(channel); +bool Context::Initialize(gfx::NativeViewId view, const gfx::Size& size) { + DCHECK(size.width() >= 0 && size.height() >= 0); - if (!channel->ready()) + if (!channel_->ready()) return false; - channel_ = channel; - // Ensure the gles2 library is initialized first in a thread safe way. Singleton<GLES2Initializer>::get(); + // Allocate a frame buffer ID with respect to the parent. + if (parent_) { + parent_->gles2_implementation_->MakeIds(1, &parent_texture_id_); + } + // Create a proxy to a command buffer in the GPU process. - command_buffer_ = channel_->CreateCommandBuffer(); + if (view) { + command_buffer_ = channel_->CreateViewCommandBuffer(view); + } else { + CommandBufferProxy* parent_command_buffer = + parent_ ? parent_->command_buffer_ : NULL; + command_buffer_ = channel_->CreateOffscreenCommandBuffer( + parent_command_buffer, + size, + parent_texture_id_); + } if (!command_buffer_) { Destroy(); return false; @@ -151,7 +177,15 @@ bool Context::Initialize(GpuChannelHost* channel) { return true; } +void Context::ResizeOffscreen(const gfx::Size& size) { + DCHECK(size.width() > 0 && size.height() > 0); + command_buffer_->ResizeOffscreenFrameBuffer(size); +} + void Context::Destroy() { + if (parent_ && parent_texture_id_ != 0) + parent_->gles2_implementation_->FreeIds(1, &parent_texture_id_); + delete gles2_implementation_; gles2_implementation_ = NULL; @@ -213,10 +247,10 @@ Error Context::GetError() { #endif // ENABLE_GPU -Context* CreateContext(GpuChannelHost* channel) { +Context* CreateViewContext(GpuChannelHost* channel, gfx::NativeViewId view) { #if defined(ENABLE_GPU) - scoped_ptr<Context> context(new Context); - if (!context->Initialize(channel)) + scoped_ptr<Context> context(new Context(channel, NULL)); + if (!context->Initialize(view, gfx::Size())) return NULL; return context.release(); @@ -225,6 +259,34 @@ Context* CreateContext(GpuChannelHost* channel) { #endif } +Context* CreateOffscreenContext(GpuChannelHost* channel, + Context* parent, + const gfx::Size& size) { +#if defined(ENABLE_GPU) + scoped_ptr<Context> context(new Context(channel, parent)); + if (!context->Initialize(NULL, size)) + return NULL; + + return context.release(); +#else + return NULL; +#endif +} + +void ResizeOffscreenContext(Context* context, const gfx::Size& size) { +#if defined(ENABLE_GPU) + context->ResizeOffscreen(size); +#endif +} + +uint32 GetParentTextureId(Context* context) { +#if defined(ENABLE_GPU) + return context->parent_texture_id(); +#else + return 0; +#endif +} + bool MakeCurrent(Context* context) { #if defined(ENABLE_GPU) return Context::MakeCurrent(context); diff --git a/chrome/renderer/ggl/ggl.h b/chrome/renderer/ggl/ggl.h index 26607ec..e5c661f 100644 --- a/chrome/renderer/ggl/ggl.h +++ b/chrome/renderer/ggl/ggl.h @@ -10,6 +10,9 @@ #ifndef CHROME_RENDERER_GGL_GGL_H_ #define CHROME_RENDERER_GGL_GGL_H_ +#include "gfx/native_widget_types.h" +#include "gfx/size.h" + class GpuChannelHost; namespace ggl { @@ -32,8 +35,30 @@ bool Initialize(); // have completed. bool Terminate(); -// Create A GGL context for an offscreen 1 x 1 pbuffer. -Context* CreateContext(GpuChannelHost* channel); +// Create a GGL context that renders directly to a view. +Context* CreateViewContext(GpuChannelHost* channel, gfx::NativeViewId view); + +// Create a GGL context that renders to an offscreen frame buffer. If parent is +// not NULL, that context can access a copy of the created +// context's frame buffer that is updated every time SwapBuffers is called. It +// is not as general as shared contexts in other implementations of OpenGL. If +// parent is not NULL, it must be used on the same thread as the parent. A child +// context may not outlive its parent. +Context* CreateOffscreenContext(GpuChannelHost* channel, + Context* parent, + const gfx::Size& size); + +// Resize an offscreen frame buffer. The resize occurs on the next call to +// SwapBuffers. This is to avoid waiting until all pending GL calls have been +// executed by the GPU process. Everything rendered up to the call to +// SwapBuffers will be lost. A lost context will be reported if the resize +// fails. +void ResizeOffscreenContext(Context* context, const gfx::Size& size); + +// For an offscreen frame buffer context, return the texture ID with +// respect to the parent context. Returns zero if context does not have a +// parent. +uint32 GetParentTextureId(Context* context); // Set the current GGL context for the calling thread. bool MakeCurrent(Context* context); @@ -41,7 +66,10 @@ bool MakeCurrent(Context* context); // Get the calling thread's current GGL context. Context* GetCurrentContext(); -// Display everything that has been rendered since the last call. +// For a view context, display everything that has been rendered since the +// last call. For an offscreen context, resolve everything that has been +// rendered since the last call to a copy that can be accessed by the parent +// context. bool SwapBuffers(); // Destroy the given GGL context. diff --git a/chrome/renderer/gpu_channel_host.cc b/chrome/renderer/gpu_channel_host.cc index 8e48b85..8d2cdd9 100644 --- a/chrome/renderer/gpu_channel_host.cc +++ b/chrome/renderer/gpu_channel_host.cc @@ -64,14 +64,43 @@ bool GpuChannelHost::Send(IPC::Message* message) { return channel_->Send(message); } -CommandBufferProxy* GpuChannelHost::CreateCommandBuffer() { +CommandBufferProxy* GpuChannelHost::CreateViewCommandBuffer( + gfx::NativeViewId view) { #if defined(ENABLE_GPU) // An error occurred. Need to get the host again to reinitialize it. if (!channel_.get()) return NULL; int32 route_id; - if (!Send(new GpuChannelMsg_CreateCommandBuffer(&route_id)) && + if (!Send(new GpuChannelMsg_CreateViewCommandBuffer(view, &route_id)) && + route_id != MSG_ROUTING_NONE) { + return NULL; + } + + CommandBufferProxy* command_buffer = new CommandBufferProxy(this, route_id); + router_.AddRoute(route_id, command_buffer); + proxies_[route_id] = command_buffer; + return command_buffer; +#else + return NULL; +#endif +} + +CommandBufferProxy* GpuChannelHost::CreateOffscreenCommandBuffer( + CommandBufferProxy* parent, + const gfx::Size& size, + uint32 parent_texture_id) { +#if defined(ENABLE_GPU) + // An error occurred. Need to get the host again to reinitialize it. + if (!channel_.get()) + return NULL; + + int32 parent_route_id = parent ? parent->route_id() : 0; + int32 route_id; + if (!Send(new GpuChannelMsg_CreateOffscreenCommandBuffer(parent_route_id, + size, + parent_texture_id, + &route_id)) && route_id != MSG_ROUTING_NONE) { return NULL; } @@ -99,3 +128,4 @@ void GpuChannelHost::DestroyCommandBuffer(CommandBufferProxy* command_buffer) { delete command_buffer; #endif } + diff --git a/chrome/renderer/gpu_channel_host.h b/chrome/renderer/gpu_channel_host.h index 00a6129..4b982bf 100644 --- a/chrome/renderer/gpu_channel_host.h +++ b/chrome/renderer/gpu_channel_host.h @@ -9,6 +9,8 @@ #include "base/hash_tables.h" #include "chrome/common/message_router.h" +#include "gfx/native_widget_types.h" +#include "gfx/size.h" #include "ipc/ipc_channel.h" #include "ipc/ipc_message.h" #include "ipc/ipc_sync_channel.h" @@ -52,7 +54,13 @@ class GpuChannelHost : public IPC::Channel::Listener, virtual bool Send(IPC::Message* msg); // Create and connect to a command buffer in the GPU process. - CommandBufferProxy* CreateCommandBuffer(); + CommandBufferProxy* CreateViewCommandBuffer(gfx::NativeViewId view); + + // Create and connect to a command buffer in the GPU process. + CommandBufferProxy* CreateOffscreenCommandBuffer(CommandBufferProxy* parent, + const gfx::Size& size, + uint32 parent_texture_id); + // Destroy a command buffer created by this channel. void DestroyCommandBuffer(CommandBufferProxy* command_buffer); diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index ab97f2a..4fb0971 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -90,6 +90,10 @@ #include "chrome/app/breakpad_mac.h" #endif +#if defined(OS_POSIX) +#include "ipc/ipc_channel_posix.h" +#endif + using WebKit::WebCache; using WebKit::WebCrossOriginPreflightResultCache; using WebKit::WebFontCache; @@ -565,6 +569,7 @@ void RenderThread::OnControlMessageReceived(const IPC::Message& msg) { OnSpellCheckWordAdded) IPC_MESSAGE_HANDLER(ViewMsg_SpellChecker_EnableAutoSpellCorrect, OnSpellCheckEnableAutoSpellCorrect) + IPC_MESSAGE_HANDLER(ViewMsg_GpuChannelEstablished, OnGpuChannelEstablished) IPC_END_MESSAGE_MAP() } @@ -680,6 +685,34 @@ void RenderThread::UpdateActiveExtensions() { child_process_logging::SetActiveExtensions(active_extensions); } +void RenderThread::EstablishGpuChannel() { + if (gpu_channel_.get()) { + // Do nothing if we are already establishing GPU channel. + if (gpu_channel_->state() == GpuChannelHost::UNCONNECTED) + return; + + // Recreate the channel if it has been lost. + if (gpu_channel_->state() == GpuChannelHost::LOST) + gpu_channel_ = NULL; + } + + if (!gpu_channel_.get()) + gpu_channel_ = new GpuChannelHost; + + // Ask the browser for the channel name. + Send(new ViewHostMsg_EstablishGpuChannel()); +} + +GpuChannelHost* RenderThread::GetGpuChannel() { + if (!gpu_channel_.get()) + return NULL; + + if (gpu_channel_->state() != GpuChannelHost::CONNECTED) + return NULL; + + return gpu_channel_.get(); +} + static void* CreateHistogram( const char *name, int min, int max, size_t buckets) { if (min <= 0) @@ -940,3 +973,20 @@ void RenderThread::OnSpellCheckEnableAutoSpellCorrect(bool enable) { void RenderThread::OnSetIsIncognitoProcess(bool is_incognito_process) { is_incognito_process_ = is_incognito_process; } + +void RenderThread::OnGpuChannelEstablished( + const IPC::ChannelHandle& channel_handle) { +#if defined(OS_POSIX) + // If we received a ChannelHandle, register it now. + if (channel_handle.socket.fd >= 0) + IPC::AddChannelSocket(channel_handle.name, channel_handle.socket.fd); +#endif + + if (channel_handle.name.size() != 0) { + // Connect to the GPU process if a channel name was received. + gpu_channel_->Connect(channel_handle.name); + } else { + // Otherwise cancel the connection. + gpu_channel_ = NULL; + } +} diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index fea7e41..3caed00 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -18,9 +18,11 @@ #include "chrome/common/child_thread.h" #include "chrome/common/css_colors.h" #include "chrome/common/dom_storage_common.h" +#include "chrome/renderer/gpu_channel_host.h" #include "chrome/renderer/renderer_histogram_snapshots.h" #include "chrome/renderer/visitedlink_slave.h" #include "gfx/native_widget_types.h" +#include "ipc/ipc_channel_handle.h" #include "ipc/ipc_platform_file.h" class AppCacheDispatcher; @@ -165,6 +167,15 @@ class RenderThread : public RenderThreadBase, // Update the list of active extensions that will be reported when we crash. void UpdateActiveExtensions(); + // Asynchronously establish a channel to the GPU plugin if not previously + // established or if it has been lost (for example if the GPU plugin crashed). + // Use GetGpuChannel() to determine when the channel is ready for use. + void EstablishGpuChannel(); + + // Get the GPU channel. Returns NULL if the channel is not established or + // has been lost. + GpuChannelHost* GetGpuChannel(); + private: virtual void OnControlMessageReceived(const IPC::Message& msg); @@ -220,6 +231,8 @@ class RenderThread : public RenderThreadBase, void OnSpellCheckWordAdded(const std::string& word); void OnSpellCheckEnableAutoSpellCorrect(bool enable); + void OnGpuChannelEstablished(const IPC::ChannelHandle& channel_handle); + // Gather usage statistics from the in-memory cache and inform our host. // These functions should be call periodically so that the host can make // decisions about how to allocation resources using current information. @@ -288,6 +301,9 @@ class RenderThread : public RenderThreadBase, // not idle, to ensure that IdleHandle gets called eventually. base::RepeatingTimer<RenderThread> forced_idle_timer_; + // The channel from the renderer process to the GPU process. + scoped_refptr<GpuChannelHost> gpu_channel_; + DISALLOW_COPY_AND_ASSIGN(RenderThread); }; diff --git a/chrome/renderer/render_widget.cc b/chrome/renderer/render_widget.cc index def6e47..50af08f 100644 --- a/chrome/renderer/render_widget.cc +++ b/chrome/renderer/render_widget.cc @@ -150,7 +150,6 @@ IPC_DEFINE_MESSAGE_MAP(RenderWidget) IPC_MESSAGE_HANDLER(ViewMsg_ImeSetComposition, OnImeSetComposition) IPC_MESSAGE_HANDLER(ViewMsg_Repaint, OnMsgRepaint) IPC_MESSAGE_HANDLER(ViewMsg_SetTextDirection, OnSetTextDirection) - IPC_MESSAGE_HANDLER(ViewMsg_GpuChannelEstablished, OnGpuChannelEstablished) IPC_MESSAGE_HANDLER(ViewMsg_Move_ACK, OnRequestMoveAck) IPC_MESSAGE_UNHANDLED_ERROR() IPC_END_MESSAGE_MAP() @@ -732,23 +731,6 @@ void RenderWidget::OnSetTextDirection(WebTextDirection direction) { webwidget_->setTextDirection(direction); } -void RenderWidget::OnGpuChannelEstablished( - const IPC::ChannelHandle& channel_handle) { -#if defined(OS_POSIX) - // If we received a ChannelHandle, register it now. - if (channel_handle.socket.fd >= 0) - IPC::AddChannelSocket(channel_handle.name, channel_handle.socket.fd); -#endif - - if (channel_handle.name.size() != 0) { - // Connect to the GPU process if a channel name was received. - gpu_channel_->Connect(channel_handle.name); - } else { - // Otherwise cancel the connection. - gpu_channel_ = NULL; - } -} - void RenderWidget::SetHidden(bool hidden) { if (is_hidden_ == hidden) return; @@ -892,31 +874,3 @@ void RenderWidget::CleanupWindowInPluginMoves(gfx::PluginWindowHandle window) { } } -void RenderWidget::EstablishGpuChannel() { - if (gpu_channel_.get()) { - // Do nothing if we are already establishing GPU channel. - if (gpu_channel_->state() == GpuChannelHost::UNCONNECTED) - return; - - // Recreate the channel if it has been lost. - if (gpu_channel_->state() == GpuChannelHost::LOST) - gpu_channel_ = NULL; - } - - if (!gpu_channel_.get()) - gpu_channel_ = new GpuChannelHost; - - // Ask the browser for the channel name. - CHECK(Send(new ViewHostMsg_EstablishGpuChannel(routing_id_))); -} - -GpuChannelHost* RenderWidget::GetGpuChannel() { - if (!gpu_channel_.get()) - return NULL; - - if (gpu_channel_->state() != GpuChannelHost::CONNECTED) - return NULL; - - return gpu_channel_.get(); -} - diff --git a/chrome/renderer/render_widget.h b/chrome/renderer/render_widget.h index 4759e30..4781790 100644 --- a/chrome/renderer/render_widget.h +++ b/chrome/renderer/render_widget.h @@ -10,7 +10,6 @@ #include "base/basictypes.h" #include "base/ref_counted.h" #include "base/shared_memory.h" -#include "chrome/renderer/gpu_channel_host.h" #include "chrome/renderer/paint_aggregator.h" #include "chrome/renderer/render_process.h" #include "gfx/native_widget_types.h" @@ -18,7 +17,6 @@ #include "gfx/rect.h" #include "gfx/size.h" #include "ipc/ipc_channel.h" -#include "ipc/ipc_channel_handle.h" #include "skia/ext/platform_canvas.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/WebKit/WebKit/chromium/public/WebCompositionCommand.h" @@ -107,15 +105,6 @@ class RenderWidget : public IPC::Channel::Listener, // Close the underlying WebWidget. virtual void Close(); - // Asynchronously establish a channel to the GPU plugin if not previously - // established or if it has been lost (for example if the GPU plugin crashed). - // Use GetGpuChannel() to determine when the channel is ready for use. - void EstablishGpuChannel(); - - // Get the GPU channel. Returns NULL if the channel is not established or - // has been lost. - GpuChannelHost* GetGpuChannel(); - protected: // Friend RefCounted so that the dtor can be non-public. Using this class // without ref-counting is an error. @@ -171,7 +160,6 @@ class RenderWidget : public IPC::Channel::Listener, const string16& ime_string); void OnMsgRepaint(const gfx::Size& size_to_paint); void OnSetTextDirection(WebKit::WebTextDirection direction); - void OnGpuChannelEstablished(const IPC::ChannelHandle& channel_handle); // Override point to notify derived classes that a paint has happened. // DidInitiatePaint happens when we've generated a new bitmap and sent it to @@ -332,9 +320,6 @@ class RenderWidget : public IPC::Channel::Listener, // Indicates if the next sequence of Char events should be suppressed or not. bool suppress_next_char_events_; - // The channel from the renderer process to the GPU process. - scoped_refptr<GpuChannelHost> gpu_channel_; - DISALLOW_COPY_AND_ASSIGN(RenderWidget); }; diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py index 16f084b..6d11c08 100755 --- a/gpu/command_buffer/build_gles2_cmd_buffer.py +++ b/gpu/command_buffer/build_gles2_cmd_buffer.py @@ -1178,6 +1178,11 @@ _FUNCTION_INFO = { 'cmd_args': 'GLuint shader, const char* data', }, + 'SwapBuffers': { + 'type': 'Custom', + 'impl_func': False, + 'unit_test': False, + }, 'TexImage2D': {'type': 'Manual', 'immediate': True}, 'TexParameterf': {'decoder_func': 'DoTexParameterf'}, 'TexParameteri': {'decoder_func': 'DoTexParameteri'}, @@ -1222,11 +1227,6 @@ _FUNCTION_INFO = { 'cmd_args': 'GLuint indx, GLint size, GLenum type, GLboolean normalized, ' 'GLsizei stride, GLuint offset', }, - 'SwapBuffers': { - 'impl_func': False, - 'decoder_func': 'DoSwapBuffers', - 'unit_test': False, - }, } diff --git a/gpu/command_buffer/client/cmd_buffer_helper_test.cc b/gpu/command_buffer/client/cmd_buffer_helper_test.cc index ffb8137..d754f5b 100644 --- a/gpu/command_buffer/client/cmd_buffer_helper_test.cc +++ b/gpu/command_buffer/client/cmd_buffer_helper_test.cc @@ -51,12 +51,12 @@ class CommandBufferHelperTest : public testing::Test { 0, api_mock_.get()); - scoped_refptr<GPUProcessor> gpu_processor(new GPUProcessor( + gpu_processor_.reset(new GPUProcessor( command_buffer_.get(), NULL, parser_, 1)); command_buffer_->SetPutOffsetChangeCallback(NewCallback( - gpu_processor.get(), &GPUProcessor::ProcessCommands)); + gpu_processor_.get(), &GPUProcessor::ProcessCommands)); - api_mock_->set_engine(gpu_processor.get()); + api_mock_->set_engine(gpu_processor_.get()); helper_.reset(new CommandBufferHelper(command_buffer_.get())); helper_->Initialize(); @@ -132,6 +132,7 @@ class CommandBufferHelperTest : public testing::Test { MessageLoop message_loop_; scoped_ptr<AsyncAPIMock> api_mock_; scoped_ptr<CommandBufferService> command_buffer_; + scoped_ptr<GPUProcessor> gpu_processor_; CommandParser* parser_; scoped_ptr<CommandBufferHelper> helper_; Sequence sequence_; diff --git a/gpu/command_buffer/client/fenced_allocator_test.cc b/gpu/command_buffer/client/fenced_allocator_test.cc index c47f355..8dc1b780 100644 --- a/gpu/command_buffer/client/fenced_allocator_test.cc +++ b/gpu/command_buffer/client/fenced_allocator_test.cc @@ -52,12 +52,12 @@ class BaseFencedAllocatorTest : public testing::Test { 0, api_mock_.get()); - scoped_refptr<GPUProcessor> gpu_processor(new GPUProcessor( + gpu_processor_.reset(new GPUProcessor( command_buffer_.get(), NULL, parser_, INT_MAX)); command_buffer_->SetPutOffsetChangeCallback(NewCallback( - gpu_processor.get(), &GPUProcessor::ProcessCommands)); + gpu_processor_.get(), &GPUProcessor::ProcessCommands)); - api_mock_->set_engine(gpu_processor.get()); + api_mock_->set_engine(gpu_processor_.get()); helper_.reset(new CommandBufferHelper(command_buffer_.get())); helper_->Initialize(); @@ -76,6 +76,7 @@ class BaseFencedAllocatorTest : public testing::Test { MessageLoop message_loop_; scoped_ptr<AsyncAPIMock> api_mock_; scoped_ptr<CommandBufferService> command_buffer_; + scoped_ptr<GPUProcessor> gpu_processor_; CommandParser* parser_; scoped_ptr<CommandBufferHelper> helper_; }; diff --git a/gpu/command_buffer/client/gles2_demo.cc b/gpu/command_buffer/client/gles2_demo.cc index 41c42b1..6035042 100644 --- a/gpu/command_buffer/client/gles2_demo.cc +++ b/gpu/command_buffer/client/gles2_demo.cc @@ -54,14 +54,16 @@ bool GLES2Demo::Setup(void* hwnd, int32 size) { if (!command_buffer->Initialize(size)) return NULL; - scoped_refptr<GPUProcessor> gpu_processor( - new GPUProcessor(command_buffer.get())); - if (!gpu_processor->Initialize(reinterpret_cast<HWND>(hwnd))) { + GPUProcessor* gpu_processor = new GPUProcessor(command_buffer.get()); + if (!gpu_processor->Initialize(reinterpret_cast<HWND>(hwnd), + NULL, + gfx::Size(), + 0)) { return NULL; } command_buffer->SetPutOffsetChangeCallback( - NewCallback(gpu_processor.get(), &GPUProcessor::ProcessCommands)); + NewCallback(gpu_processor, &GPUProcessor::ProcessCommands)); GLES2CmdHelper* helper = new GLES2CmdHelper(command_buffer.get()); if (!helper->Initialize()) { diff --git a/gpu/command_buffer/client/gles2_implementation.h b/gpu/command_buffer/client/gles2_implementation.h index c1ee9c6..fd98686 100644 --- a/gpu/command_buffer/client/gles2_implementation.h +++ b/gpu/command_buffer/client/gles2_implementation.h @@ -43,13 +43,13 @@ class GLES2Implementation { // this file instead of having to edit some template or the code generator. #include "../client/gles2_implementation_autogen.h" - private: // Makes a set of Ids for glGen___ functions. void MakeIds(GLsizei n, GLuint* ids); // Frees a set of Ids for glDelete___ functions. void FreeIds(GLsizei n, const GLuint* ids); + private: // Gets the shared memory id for the result buffer. uint32 result_shm_id() const { return transfer_buffer_id_; diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 512a819..23c3423 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -14,6 +14,7 @@ #include "base/callback.h" #include "base/linked_ptr.h" #include "base/scoped_ptr.h" +#include "base/weak_ptr.h" #define GLES2_GPU_SERVICE 1 #include "gpu/command_buffer/common/gles2_cmd_format.h" #include "gpu/command_buffer/common/gles2_cmd_utils.h" @@ -36,9 +37,15 @@ #include "app/surface/accelerated_surface_mac.h" #endif +#if !defined(GL_DEPTH24_STENCIL8) +#define GL_DEPTH24_STENCIL8 0x88F0 +#endif + namespace gpu { namespace gles2 { +class GLES2DecoderImpl; + // Check that certain assumptions the code makes are true. There are places in // the code where shared memory is passed direclty to GL. Example, glUniformiv, // glShaderSource. The command buffer code assumes GLint and GLsizei (and maybe @@ -107,6 +114,144 @@ const CommandInfo g_command_info[] = { #undef GLES2_CMD_OP }; +// This class prevents any GL errors that occur when it is in scope from +// being reported to the client. +class ScopedGLErrorSuppressor { + public: + explicit ScopedGLErrorSuppressor(GLES2DecoderImpl* decoder); + ~ScopedGLErrorSuppressor(); + private: + GLES2DecoderImpl* decoder_; + DISALLOW_COPY_AND_ASSIGN(ScopedGLErrorSuppressor); +}; + +// Temporarily changes a decoder's bound 2D texture and restore it when this +// object goes out of scope. Also temporarily switches to using active texture +// unit zero in case the client has changed that to something invalid. +class ScopedTexture2DBinder { + public: + ScopedTexture2DBinder(GLES2DecoderImpl* decoder, GLuint id); + ~ScopedTexture2DBinder(); + + private: + GLES2DecoderImpl* decoder_; + DISALLOW_COPY_AND_ASSIGN(ScopedTexture2DBinder); +}; + +// Temporarily changes a decoder's bound render buffer and restore it when this +// object goes out of scope. +class ScopedRenderBufferBinder { + public: + ScopedRenderBufferBinder(GLES2DecoderImpl* decoder, GLuint id); + ~ScopedRenderBufferBinder(); + + private: + GLES2DecoderImpl* decoder_; + DISALLOW_COPY_AND_ASSIGN(ScopedRenderBufferBinder); +}; + +// Temporarily changes a decoder's bound frame buffer and restore it when this +// object goes out of scope. +class ScopedFrameBufferBinder { + public: + ScopedFrameBufferBinder(GLES2DecoderImpl* decoder, GLuint id); + ~ScopedFrameBufferBinder(); + + private: + GLES2DecoderImpl* decoder_; + DISALLOW_COPY_AND_ASSIGN(ScopedFrameBufferBinder); +}; + +// Encapsulates an OpenGL texture. +class Texture { + public: + explicit Texture(GLES2DecoderImpl* decoder); + ~Texture(); + + // Create a new render texture. + void Create(); + + // Set the initial size and format of a render texture or resize it. + bool AllocateStorage(const gfx::Size& size); + + // Copy the contents of the currently bound frame buffer. + void Copy(const gfx::Size& size); + + // Destroy the render texture. This must be explicitly called before + // destroying this object. + void Destroy(); + + GLuint id() const { + return id_; + } + + private: + GLES2DecoderImpl* decoder_; + GLuint id_; + DISALLOW_COPY_AND_ASSIGN(Texture); +}; + +// Encapsulates an OpenGL render buffer of any format. +class RenderBuffer { + public: + explicit RenderBuffer(GLES2DecoderImpl* decoder); + ~RenderBuffer(); + + // Create a new render buffer. + void Create(); + + // Set the initial size and format of a render buffer or resize it. + bool AllocateStorage(const gfx::Size& size, GLenum format); + + // Destroy the render buffer. This must be explicitly called before destroying + // this object. + void Destroy(); + + GLuint id() const { + return id_; + } + + private: + GLES2DecoderImpl* decoder_; + GLuint id_; + DISALLOW_COPY_AND_ASSIGN(RenderBuffer); +}; + +// Encapsulates an OpenGL frame buffer. +class FrameBuffer { + public: + explicit FrameBuffer(GLES2DecoderImpl* decoder); + ~FrameBuffer(); + + // Create a new frame buffer. + void Create(); + + // Attach a color render buffer to a frame buffer. + void AttachRenderTexture(Texture* texture); + + // Attach a depth stencil render buffer to a frame buffer. Note that + // this unbinds any currently bound frame buffer. + void AttachDepthStencilRenderBuffer(RenderBuffer* render_buffer); + + // Clear the given attached buffers. + void Clear(GLbitfield buffers); + + // Destroy the frame buffer. This must be explicitly called before destroying + // this object. + void Destroy(); + + // See glCheckFramebufferStatusEXT. + GLenum CheckStatus(); + + GLuint id() const { + return id_; + } + + private: + GLES2DecoderImpl* decoder_; + GLuint id_; + DISALLOW_COPY_AND_ASSIGN(FrameBuffer); +}; // } // anonymous namespace. GLES2Decoder::GLES2Decoder(ContextGroup* group) @@ -129,7 +274,8 @@ GLES2Decoder::~GLES2Decoder() { // This class implements GLES2Decoder so we don't have to expose all the GLES2 // cmd stuff to outside this class. -class GLES2DecoderImpl : public GLES2Decoder { +class GLES2DecoderImpl : public base::SupportsWeakPtr<GLES2DecoderImpl>, + public GLES2Decoder { public: explicit GLES2DecoderImpl(ContextGroup* group); @@ -210,8 +356,11 @@ class GLES2DecoderImpl : public GLES2Decoder { virtual const char* GetCommandName(unsigned int command_id) const; // Overridden from GLES2Decoder. - virtual bool Initialize(); + virtual bool Initialize(GLES2Decoder* parent, + const gfx::Size& size, + uint32 parent_client_texture_id); virtual void Destroy(); + virtual void ResizeOffscreenFrameBuffer(const gfx::Size& size); virtual bool MakeCurrent(); virtual uint32 GetServiceIdForTesting(uint32 client_id); virtual GLES2Util* GetGLES2Util() { return &util_; } @@ -239,6 +388,13 @@ class GLES2DecoderImpl : public GLES2Decoder { virtual void SetSwapBuffersCallback(Callback0::Type* callback); private: + friend class ScopedGLErrorSuppressor; + friend class ScopedTexture2DBinder; + friend class ScopedFrameBufferBinder; + friend class ScopedRenderBufferBinder; + friend class RenderBuffer; + friend class FrameBuffer; + // State associated with each texture unit. struct TextureUnit { TextureUnit() : bind_target(GL_TEXTURE_2D) { } @@ -309,6 +465,8 @@ class GLES2DecoderImpl : public GLES2Decoder { static bool InitGlew(); void DestroyPlatformSpecific(); + bool UpdateOffscreenFrameBufferSize(); + // Template to help call glGenXXX functions. template <void gl_gen_function(GLES2DecoderImpl*, GLsizei, GLuint*)> bool GenGLObjects(GLsizei n, const GLuint* client_ids) { @@ -343,8 +501,8 @@ class GLES2DecoderImpl : public GLES2Decoder { GLsizei n, const GLuint* client_ids, GLuint* service_ids); // Creates a TextureInfo for the given texture. - void CreateTextureInfo(GLuint texture) { - texture_manager()->CreateTextureInfo(texture); + TextureManager::TextureInfo* CreateTextureInfo(GLuint texture) { + return texture_manager()->CreateTextureInfo(texture); } // Gets the texture info for the given texture. Returns NULL if none exists. @@ -561,9 +719,6 @@ class GLES2DecoderImpl : public GLES2Decoder { void DoRenderbufferStorage( GLenum target, GLenum internalformat, GLsizei width, GLsizei height); - // Swaps the buffers (copies/renders to the current window). - void DoSwapBuffers(); - // Wrappers for glTexParameter functions. void DoTexParameterf(GLenum target, GLenum pname, GLfloat param); void DoTexParameteri(GLenum target, GLenum pname, GLint param); @@ -590,6 +745,10 @@ class GLES2DecoderImpl : public GLES2Decoder { // command. void CopyRealGLErrorsToWrapper(); + // Clear all real GL errors. This is to prevent the client from seeing any + // errors caused by GL calls that it was not responsible for issuing. + void ClearRealGLErrors(); + // Checks if the current program and vertex attributes are valid for drawing. bool IsDrawValid(GLuint max_vertex_accessed); @@ -654,6 +813,17 @@ class GLES2DecoderImpl : public GLES2Decoder { #undef GLES2_CMD_OP + // A parent decoder can access this decoders saved offscreen frame buffer. + // The parent pointer is reset if the parent is destroyed. + base::WeakPtr<GLES2DecoderImpl> parent_; + + // Width and height to which an offscreen frame buffer should be resized on + // the next call to SwapBuffers. + gfx::Size pending_size_; + + // Width and height of a decoder that renders to an offscreen frame buffer. + gfx::Size current_size_; + // Current GL error bits. uint32 error_bits_; @@ -714,11 +884,252 @@ class GLES2DecoderImpl : public GLES2Decoder { bool anti_aliased_; + // The offscreen frame buffer that the client renders to. + scoped_ptr<FrameBuffer> offscreen_target_frame_buffer_; + scoped_ptr<Texture> offscreen_target_color_texture_; + scoped_ptr<RenderBuffer> offscreen_target_depth_stencil_render_buffer_; + + // The copy that is saved when SwapBuffers is called. + scoped_ptr<Texture> offscreen_saved_color_texture_; + + // A frame buffer used for rendering to render textures and render buffers + // without concern about any state the client might have changed on the frame + // buffers it has access to. + scoped_ptr<FrameBuffer> temporary_frame_buffer_; + scoped_ptr<Callback0::Type> swap_buffers_callback_; DISALLOW_COPY_AND_ASSIGN(GLES2DecoderImpl); }; +ScopedGLErrorSuppressor::ScopedGLErrorSuppressor(GLES2DecoderImpl* decoder) + : decoder_(decoder) { + decoder_->CopyRealGLErrorsToWrapper(); +} + +ScopedGLErrorSuppressor::~ScopedGLErrorSuppressor() { + decoder_->ClearRealGLErrors(); +} + +ScopedTexture2DBinder::ScopedTexture2DBinder(GLES2DecoderImpl* decoder, + GLuint id) + : decoder_(decoder) { + ScopedGLErrorSuppressor suppressor(decoder_); + + // TODO(apatrick): Check if there are any other states that need to be reset + // before binding a new texture. + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, id); +} + +ScopedTexture2DBinder::~ScopedTexture2DBinder() { + ScopedGLErrorSuppressor suppressor(decoder_); + GLES2DecoderImpl::TextureUnit& info = decoder_->texture_units_[0]; + GLuint last_id; + if (info.bound_texture_2d) + last_id = info.bound_texture_2d->texture_id(); + else + last_id = 0; + + glBindTexture(GL_TEXTURE_2D, last_id); + glActiveTexture(GL_TEXTURE0 + decoder_->active_texture_unit_); +} + +ScopedRenderBufferBinder::ScopedRenderBufferBinder(GLES2DecoderImpl* decoder, + GLuint id) + : decoder_(decoder) { + ScopedGLErrorSuppressor suppressor(decoder_); + glBindRenderbufferEXT(GL_RENDERBUFFER, id); +} + +ScopedRenderBufferBinder::~ScopedRenderBufferBinder() { + ScopedGLErrorSuppressor suppressor(decoder_); + glBindRenderbufferEXT(GL_RENDERBUFFER, decoder_->bound_renderbuffer_); +} + +ScopedFrameBufferBinder::ScopedFrameBufferBinder(GLES2DecoderImpl* decoder, + GLuint id) + : decoder_(decoder) { + ScopedGLErrorSuppressor suppressor(decoder_); + glBindFramebufferEXT(GL_FRAMEBUFFER, id); +} + +ScopedFrameBufferBinder::~ScopedFrameBufferBinder() { + ScopedGLErrorSuppressor suppressor(decoder_); + if (decoder_->bound_framebuffer_ == 0 && + decoder_->offscreen_target_frame_buffer_.get()) { + glBindFramebufferEXT(GL_FRAMEBUFFER, + decoder_->offscreen_target_frame_buffer_->id()); + } else { + glBindFramebufferEXT(GL_FRAMEBUFFER, decoder_->bound_framebuffer_); + } +} + +Texture::Texture(GLES2DecoderImpl* decoder) + : decoder_(decoder), + id_(0) { +} + +Texture::~Texture() { + // This does not destroy the render texture because that would require that + // the associated GL context was current. Just check that it was explicitly + // destroyed. + DCHECK_EQ(id_, 0u); +} + +void Texture::Create() { + ScopedGLErrorSuppressor suppressor(decoder_); + Destroy(); + glGenTextures(1, &id_); +} + +bool Texture::AllocateStorage(const gfx::Size& size) { + DCHECK_NE(id_, 0u); + ScopedGLErrorSuppressor suppressor(decoder_); + ScopedTexture2DBinder binder(decoder_, id_); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri( + GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); + + glTexImage2D(GL_TEXTURE_2D, + 0, // mip level + GL_RGBA, + size.width(), + size.height(), + 0, // border + GL_RGBA, + GL_UNSIGNED_BYTE, + NULL); + + return glGetError() == GL_NO_ERROR; +} + +void Texture::Copy(const gfx::Size& size) { + DCHECK_NE(id_, 0u); + ScopedGLErrorSuppressor suppressor(decoder_); + ScopedTexture2DBinder binder(decoder_, id_); + glCopyTexImage2D(GL_TEXTURE_2D, + 0, // level + GL_RGBA, + 0, 0, + size.width(), + size.height(), + 0); // border +} + +void Texture::Destroy() { + if (id_ != 0) { + ScopedGLErrorSuppressor suppressor(decoder_); + glDeleteTextures(1, &id_); + id_ = 0; + } +} + +RenderBuffer::RenderBuffer(GLES2DecoderImpl* decoder) + : decoder_(decoder), + id_(0) { +} + +RenderBuffer::~RenderBuffer() { + // This does not destroy the render buffer because that would require that + // the associated GL context was current. Just check that it was explicitly + // destroyed. + DCHECK_EQ(id_, 0u); +} + +void RenderBuffer::Create() { + ScopedGLErrorSuppressor suppressor(decoder_); + Destroy(); + glGenRenderbuffersEXT(1, &id_); +} + +bool RenderBuffer::AllocateStorage(const gfx::Size& size, GLenum format) { + ScopedGLErrorSuppressor suppressor(decoder_); + ScopedRenderBufferBinder binder(decoder_, id_); + glRenderbufferStorageEXT(GL_RENDERBUFFER, + format, + size.width(), + size.height()); + return glGetError() == GL_NO_ERROR; +} + +void RenderBuffer::Destroy() { + if (id_ != 0) { + ScopedGLErrorSuppressor suppressor(decoder_); + glDeleteRenderbuffersEXT(1, &id_); + id_ = 0; + } +} + +FrameBuffer::FrameBuffer(GLES2DecoderImpl* decoder) + : decoder_(decoder), + id_(0) { +} + +FrameBuffer::~FrameBuffer() { + // This does not destroy the frame buffer because that would require that + // the associated GL context was current. Just check that it was explicitly + // destroyed. + DCHECK_EQ(id_, 0u); +} + +void FrameBuffer::Create() { + ScopedGLErrorSuppressor suppressor(decoder_); + Destroy(); + glGenFramebuffersEXT(1, &id_); +} + +void FrameBuffer::AttachRenderTexture(Texture* texture) { + DCHECK_NE(id_, 0u); + ScopedGLErrorSuppressor suppressor(decoder_); + ScopedFrameBufferBinder binder(decoder_, id_); + GLuint attach_id = texture ? texture->id() : 0; + glFramebufferTexture2DEXT(GL_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + attach_id, + 0); +} + +void FrameBuffer::AttachDepthStencilRenderBuffer(RenderBuffer* render_buffer) { + DCHECK_NE(id_, 0u); + ScopedGLErrorSuppressor suppressor(decoder_); + ScopedFrameBufferBinder binder(decoder_, id_); + GLuint attach_id = render_buffer ? render_buffer->id() : 0; + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + attach_id); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER, + GL_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, + attach_id); +} + +void FrameBuffer::Clear(GLbitfield buffers) { + ScopedGLErrorSuppressor suppressor(decoder_); + ScopedFrameBufferBinder binder(decoder_, id_); + glClear(buffers); +} + +void FrameBuffer::Destroy() { + if (id_ != 0) { + ScopedGLErrorSuppressor suppressor(decoder_); + glDeleteFramebuffersEXT(1, &id_); + id_ = 0; + } +} + +GLenum FrameBuffer::CheckStatus() { + DCHECK_NE(id_, 0u); + ScopedGLErrorSuppressor suppressor(decoder_); + ScopedFrameBufferBinder binder(decoder_, id_); + return glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); +} + GLES2Decoder* GLES2Decoder::Create(ContextGroup* group) { return new GLES2DecoderImpl(group); } @@ -750,11 +1161,21 @@ GLES2DecoderImpl::GLES2DecoderImpl(ContextGroup* group) anti_aliased_(false) { } -bool GLES2DecoderImpl::Initialize() { +bool GLES2DecoderImpl::Initialize(GLES2Decoder* parent, + const gfx::Size& size, + uint32 parent_client_texture_id) { + // Keep only a weak pointer to the parent so we don't unmap its client + // frame buffer is after it has been destroyed. + if (parent) + parent_ = static_cast<GLES2DecoderImpl*>(parent)->AsWeakPtr(); + + pending_size_ = size; + if (!InitPlatformSpecific()) { Destroy(); return false; } + if (!MakeCurrent()) { Destroy(); return false; @@ -806,6 +1227,52 @@ bool GLES2DecoderImpl::Initialize() { glBindTexture(GL_TEXTURE_CUBE_MAP, 0); CHECK_GL_ERROR(); + if (size.width() > 0 && size.height() > 0) { + // Create the target frame buffer. This is the one that the client renders + // directly to. + offscreen_target_frame_buffer_.reset(new FrameBuffer(this)); + offscreen_target_frame_buffer_->Create(); + offscreen_target_color_texture_.reset(new Texture(this)); + offscreen_target_color_texture_->Create(); + offscreen_target_depth_stencil_render_buffer_.reset( + new RenderBuffer(this)); + offscreen_target_depth_stencil_render_buffer_->Create(); + + // Create the saved offscreen texture. The target frame buffer is copied + // here when SwapBuffers is called. + offscreen_saved_color_texture_.reset(new Texture(this)); + offscreen_saved_color_texture_->Create(); + + // Create the temporary frame buffer, used to operate on render textures + // without concern for state the client might have changed on the frame + // buffers it has access to, like the clear color and the color mask. + temporary_frame_buffer_.reset(new FrameBuffer(this)); + temporary_frame_buffer_->Create(); + + // Map the ID of the saved offscreen texture into the parent so that + // it can reference it. + if (parent_) { + GLuint service_id = offscreen_saved_color_texture_->id(); + parent_->id_manager()->AddMapping(parent_client_texture_id, + service_id); + TextureManager::TextureInfo* info = + parent_->CreateTextureInfo(service_id); + parent_->texture_manager()->SetInfoTarget(info, GL_TEXTURE_2D); + } + + // Allocate the render buffers at their initial size and check the status + // of the frame buffers is okay. + if (!UpdateOffscreenFrameBufferSize()) { + DLOG(ERROR) << "Could not allocate offscreen buffer storage."; + Destroy(); + return false; + } + + // Bind to the new default frame buffer (the offscreen target frame buffer). + // This should now be associated with ID zero. + DoBindFramebuffer(GL_FRAMEBUFFER, 0); + } + return true; } @@ -919,7 +1386,7 @@ bool GLES2DecoderImpl::InitializeOneOff(bool anti_aliased) { // GL context was successfully created and applied to the window's DC. // Startup GLEW, the GL extensions wrangler. if (InitGlew()) { - DLOG(INFO) << "Initialized GLEW " << ::glewGetString(GLEW_VERSION); + DLOG(INFO) << "Initialized GLEW " << glewGetString(GLEW_VERSION); } else { ::wglMakeCurrent(intermediate_dc, NULL); ::wglDeleteContext(gl_context); @@ -1124,6 +1591,7 @@ void GLES2DecoderImpl::UnregisterObjects( } bool GLES2DecoderImpl::InitPlatformSpecific() { + bool offscreen = pending_size_.width() > 0 && pending_size_.height() > 0; #if defined(UNIT_TEST) #elif defined(GLES2_GPU_SERVICE_BACKEND_NATIVE_GLES2) #elif defined(OS_WIN) @@ -1132,22 +1600,13 @@ bool GLES2DecoderImpl::InitPlatformSpecific() { if (!success) return false; - if (hwnd()) { - // The GL context will render to this window. - gl_device_context_ = ::GetDC(hwnd()); - - if (!::SetPixelFormat(gl_device_context_, - pixel_format_, - &kPixelFormatDescriptor)) { - DLOG(ERROR) << "Unable to set the pixel format for GL context."; - DestroyPlatformSpecific(); - return false; - } - } else { + if (offscreen) { // Create a device context compatible with the primary display. HDC display_device_context = ::CreateDC(L"DISPLAY", NULL, NULL, NULL); - // Create a 1 x 1 pbuffer suitable for use with the device. + // Create a 1 x 1 pbuffer suitable for use with the device. This is just + // a stepping stone towards creating a frame buffer object. It doesn't + // matter what size it is. const int kNoAttributes[] = { 0 }; pbuffer_ = ::wglCreatePbufferARB(display_device_context, pixel_format_, @@ -1166,6 +1625,17 @@ bool GLES2DecoderImpl::InitPlatformSpecific() { DestroyPlatformSpecific(); return false; } + } else { + // The GL context will render to this window. + gl_device_context_ = ::GetDC(hwnd()); + + if (!::SetPixelFormat(gl_device_context_, + pixel_format_, + &kPixelFormatDescriptor)) { + DLOG(ERROR) << "Unable to set the pixel format for GL context."; + DestroyPlatformSpecific(); + return false; + } } gl_context_ = ::wglCreateContext(gl_device_context_); @@ -1174,11 +1644,32 @@ bool GLES2DecoderImpl::InitPlatformSpecific() { DestroyPlatformSpecific(); return false; } + + if (parent_) { + if (!wglShareLists(parent_->gl_context_, gl_context_)) { + DLOG(ERROR) << "Could not share GL contexts."; + DestroyPlatformSpecific(); + return false; + } + } + #elif defined(OS_LINUX) + // TODO(apatrick): offscreen rendering not yet supported on this platform. + DCHECK(!offscreen); + + // TODO(apatrick): parent contexts not yet supported on this platform. + DCHECK(!parent_); + DCHECK(window()); if (!window()->Initialize()) return false; #elif defined(OS_MACOSX) + // TODO(apatrick): offscreen rendering not yet supported on this platform. + DCHECK(!offscreen); + + // TODO(apatrick): parent contexts not yet supported on this platform. + DCHECK(!parent_); + return surface_.Initialize(); #endif @@ -1192,7 +1683,7 @@ bool GLES2DecoderImpl::InitGlew() { GLenum glew_error = glewInit(); if (glew_error != GLEW_OK) { DLOG(ERROR) << "Unable to initialise GLEW : " - << ::glewGetErrorString(glew_error); + << glewGetErrorString(glew_error); return false; } @@ -1265,6 +1756,85 @@ void GLES2DecoderImpl::DestroyPlatformSpecific() { #endif } +bool GLES2DecoderImpl::UpdateOffscreenFrameBufferSize() { + if (current_size_ != pending_size_) + return true; + + // Reallocate the offscreen target buffers. + if (!offscreen_target_color_texture_->AllocateStorage(pending_size_)) { + return false; + } + + if (!offscreen_target_depth_stencil_render_buffer_->AllocateStorage( + pending_size_, GL_DEPTH24_STENCIL8)) { + return false; + } + + // Attach the offscreen target buffers to the temporary frame buffer + // so they can be cleared using that frame buffer's clear parameters (all + // zero, no color mask, etc). + temporary_frame_buffer_->AttachRenderTexture( + offscreen_target_color_texture_.get()); + temporary_frame_buffer_->AttachDepthStencilRenderBuffer( + offscreen_target_depth_stencil_render_buffer_.get()); + if (temporary_frame_buffer_->CheckStatus() != + GL_FRAMEBUFFER_COMPLETE) { + return false; + } + + // Clear the offscreen target buffers to all zero (using the saved frame + // buffer they are temporarily attached to). + temporary_frame_buffer_->Clear( + GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + + // Detach the offscreen target buffer. + temporary_frame_buffer_->AttachRenderTexture(NULL); + temporary_frame_buffer_->AttachDepthStencilRenderBuffer(NULL); + + // Attach the offscreen target buffers to the proper frame buffer. + offscreen_target_frame_buffer_->AttachRenderTexture( + offscreen_target_color_texture_.get()); + offscreen_target_frame_buffer_->AttachDepthStencilRenderBuffer( + offscreen_target_depth_stencil_render_buffer_.get()); + if (offscreen_target_frame_buffer_->CheckStatus() != + GL_FRAMEBUFFER_COMPLETE) { + return false; + } + + // Create the saved offscreen color texture. + offscreen_saved_color_texture_->AllocateStorage(pending_size_); + + // Clear the offscreen saved color texture by copying the cleared target + // frame buffer into it. + { + ScopedFrameBufferBinder binder(this, offscreen_target_frame_buffer_->id()); + offscreen_saved_color_texture_->Copy(pending_size_); + } + + // Update the info about the offscreen saved color texture in the parent. + // The reference to the parent is a weak pointer and will become null if the + // parent is later destroyed. + if (parent_) { + GLuint service_id = offscreen_saved_color_texture_->id(); + + TextureManager::TextureInfo* info = + parent_->texture_manager()->GetTextureInfo(service_id); + DCHECK(info); + + info->SetLevelInfo(GL_TEXTURE_2D, + 0, // level + GL_RGBA, + pending_size_.width(), pending_size_.height(), + 1, // depth + 0, // border + GL_RGBA, + GL_UNSIGNED_BYTE); + } + + current_size_ = pending_size_; + return true; +} + #if defined(OS_MACOSX) uint64 GLES2DecoderImpl::SetWindowSizeForIOSurface(int32 width, int32 height) { @@ -1300,6 +1870,37 @@ void GLES2DecoderImpl::SetSwapBuffersCallback(Callback0::Type* callback) { } void GLES2DecoderImpl::Destroy() { + MakeCurrent(); + + // Remove the saved frame buffer mapping from the parent decoder. The + // parent pointer is a weak pointer so it will be null if the parent has + // already been destroyed. + if (parent_) { + // First check the texture has been mapped into the parent. This might not + // be the case if initialization failed midway through. + GLuint service_id = offscreen_saved_color_texture_->id(); + GLuint client_id; + if (parent_->id_manager()->GetClientId(service_id, &client_id)) { + parent_->texture_manager()->RemoveTextureInfo(service_id); + parent_->id_manager()->RemoveMapping(client_id, service_id); + } + } + + if (offscreen_target_frame_buffer_.get()) + offscreen_target_frame_buffer_->Destroy(); + + if (offscreen_target_color_texture_.get()) + offscreen_target_color_texture_->Destroy(); + + if (offscreen_target_depth_stencil_render_buffer_.get()) + offscreen_target_depth_stencil_render_buffer_->Destroy(); + + if (temporary_frame_buffer_.get()) + temporary_frame_buffer_->Destroy(); + + if (offscreen_saved_color_texture_.get()) + offscreen_saved_color_texture_->Destroy(); + #if defined(UNIT_TEST) #elif defined(GLES2_GPU_SERVICE_BACKEND_NATIVE_GLES2) #elif defined(OS_LINUX) @@ -1312,6 +1913,13 @@ void GLES2DecoderImpl::Destroy() { DestroyPlatformSpecific(); } +void GLES2DecoderImpl::ResizeOffscreenFrameBuffer(const gfx::Size& size) { + // We can't resize the render buffers immediately because there might be a + // partial frame rendered into them and we don't want the tail end of that + // rendered into the reallocated storage. Defer until the next SwapBuffers. + pending_size_ = size; +} + const char* GLES2DecoderImpl::GetCommandName(unsigned int command_id) const { if (command_id > kStartPoint && command_id < kNumCommands) { return gles2::GetCommandName(static_cast<CommandId>(command_id)); @@ -1452,6 +2060,12 @@ void GLES2DecoderImpl::DoBindBuffer(GLenum target, GLuint buffer) { void GLES2DecoderImpl::DoBindFramebuffer(GLenum target, GLuint framebuffer) { bound_framebuffer_ = framebuffer; + + // When rendering to an offscreen frame buffer, instead of unbinding from + // the current frame buffer, bind to the offscreen target frame buffer. + if (framebuffer == 0 && offscreen_target_frame_buffer_.get()) + framebuffer = offscreen_target_frame_buffer_->id(); + glBindFramebufferEXT(target, framebuffer); } @@ -1685,24 +2299,6 @@ void GLES2DecoderImpl::DoLinkProgram(GLuint program) { } }; -void GLES2DecoderImpl::DoSwapBuffers() { -#if defined(UNIT_TEST) -#elif defined(GLES2_GPU_SERVICE_BACKEND_NATIVE_GLES2) -#elif defined(OS_WIN) - ::SwapBuffers(gl_device_context_); -#elif defined(OS_LINUX) - DCHECK(window()); - window()->SwapBuffers(); -#elif defined(OS_MACOSX) - // TODO(kbr): Need to property hook up and track the OpenGL state and hook - // up the notion of the currently bound FBO. - surface_.SwapBuffers(); -#endif - if (swap_buffers_callback_.get()) { - swap_buffers_callback_->Run(); - } -} - void GLES2DecoderImpl::DoTexParameterf( GLenum target, GLenum pname, GLfloat param) { TextureManager::TextureInfo* info = GetTextureInfoForTarget(target); @@ -1812,6 +2408,13 @@ void GLES2DecoderImpl::CopyRealGLErrorsToWrapper() { } } +void GLES2DecoderImpl::ClearRealGLErrors() { + GLenum error; + while ((error = glGetError()) != GL_NO_ERROR) { + NOTREACHED() << "GL error " << error << " was unhandled."; + } +} + bool GLES2DecoderImpl::VertexAttribInfo::CanAccess(GLuint index) { if (!enabled_) { return true; @@ -2981,6 +3584,48 @@ error::Error GLES2DecoderImpl::HandleGetActiveAttrib( return error::kNoError; } +error::Error GLES2DecoderImpl::HandleSwapBuffers( + uint32 immediate_data_size, const gles2::SwapBuffers& c) { + // Check a client created frame buffer is not bound. TODO(apatrick): + // this error is overkill. It will require that the client recreate the + // context to continue. + if (bound_framebuffer_ != 0) + return error::kLostContext; + + // If offscreen then don't actually SwapBuffers to the display. Just copy + // the rendered frame to another frame buffer. + if (offscreen_target_frame_buffer_.get()) { + ScopedGLErrorSuppressor suppressor(this); + + // First check to see if a deferred offscreen render buffer resize is + // pending. + if (!UpdateOffscreenFrameBufferSize()) + return error::kLostContext; + + ScopedFrameBufferBinder binder(this, offscreen_target_frame_buffer_->id()); + offscreen_saved_color_texture_->Copy(current_size_); + } else { +#if defined(UNIT_TEST) +#elif defined(GLES2_GPU_SERVICE_BACKEND_NATIVE_GLES2) +#elif defined(OS_WIN) + ::SwapBuffers(gl_device_context_); +#elif defined(OS_LINUX) + DCHECK(window()); + window()->SwapBuffers(); +#elif defined(OS_MACOSX) + // TODO(kbr): Need to property hook up and track the OpenGL state and hook + // up the notion of the currently bound FBO. + surface_.SwapBuffers(); +#endif + } + + if (swap_buffers_callback_.get()) { + swap_buffers_callback_->Run(); + } + + return error::kNoError; +} + // Include the auto-generated part of this file. We split this because it means // we can easily edit the non-auto generated parts right here in this file // instead of having to edit some template or the code generator. diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.h b/gpu/command_buffer/service/gles2_cmd_decoder.h index 26c6239..58e1193 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder.h @@ -15,6 +15,8 @@ #if defined(OS_MACOSX) #include "app/surface/transport_dib.h" #endif + +#include "gfx/size.h" #include "gpu/command_buffer/service/common_decoder.h" @@ -71,14 +73,20 @@ class GLES2Decoder : public CommonDecoder { Callback1<TransportDIB::Id>::Type* deallocator) = 0; #endif - // Initializes the graphics context. + // Initializes the graphics context. Can create an offscreen + // decoder with a frame buffer that can be referenced from the parent. // Returns: // true if successful. - virtual bool Initialize() = 0; + virtual bool Initialize(GLES2Decoder* parent, + const gfx::Size& size, + uint32 parent_texture_id) = 0; // Destroys the graphics context. virtual void Destroy() = 0; + // Resize an offscreen frame buffer. + virtual void ResizeOffscreenFrameBuffer(const gfx::Size& size) = 0; + // Make this decoder's GL context current. virtual bool MakeCurrent() = 0; diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h index cb534a4..7507d95 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h @@ -2842,11 +2842,5 @@ error::Error GLES2DecoderImpl::HandleViewport( return error::kNoError; } -error::Error GLES2DecoderImpl::HandleSwapBuffers( - uint32 immediate_data_size, const gles2::SwapBuffers& c) { - DoSwapBuffers(); - return error::kNoError; -} - #endif // GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_AUTOGEN_H_ diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h index 2ef2d2d..f94eb7d 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_mock.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_mock.h @@ -7,6 +7,7 @@ #ifndef GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_MOCK_H_ #define GPU_COMMAND_BUFFER_SERVICE_GLES2_CMD_DECODER_MOCK_H_ +#include "gfx/size.h" #include "gpu/command_buffer/service/gles2_cmd_decoder.h" #include "base/callback.h" #include "testing/gmock/include/gmock/gmock.h" @@ -28,8 +29,11 @@ class MockGLES2Decoder : public GLES2Decoder { #if defined(OS_MACOSX) MOCK_METHOD2(SetWindowSize, uint64(int32 width, int32 height)); #endif - MOCK_METHOD0(Initialize, bool()); + MOCK_METHOD3(Initialize, bool(GLES2Decoder* parent, + const gfx::Size& size, + uint32 parent_texture_id)); MOCK_METHOD0(Destroy, void()); + MOCK_METHOD1(ResizeOffscreenFrameBuffer, void(const gfx::Size& size)); MOCK_METHOD0(MakeCurrent, bool()); MOCK_METHOD1(GetServiceIdForTesting, uint32(uint32 client_id)); MOCK_METHOD0(GetGLES2Util, GLES2Util*()); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc index 0bb32c6..10acd1b 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc @@ -92,7 +92,7 @@ void GLES2DecoderTestBase::SetUp() { shared_memory_id_ = kSharedMemoryId; decoder_.reset(GLES2Decoder::Create(&group_)); - decoder_->Initialize(); + decoder_->Initialize(NULL, gfx::Size(), 0); decoder_->set_engine(engine_.get()); EXPECT_CALL(*gl_, GenBuffersARB(_, _)) diff --git a/gpu/command_buffer/service/gpu_processor.cc b/gpu/command_buffer/service/gpu_processor.cc index 7a748e7..d08069c 100644 --- a/gpu/command_buffer/service/gpu_processor.cc +++ b/gpu/command_buffer/service/gpu_processor.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/callback.h" +#include "base/compiler_specific.h" #include "base/message_loop.h" #include "gpu/command_buffer/service/gpu_processor.h" @@ -12,7 +13,8 @@ namespace gpu { GPUProcessor::GPUProcessor(CommandBuffer* command_buffer) : command_buffer_(command_buffer), - commands_per_update_(100) { + commands_per_update_(100), + method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { DCHECK(command_buffer); decoder_.reset(gles2::GLES2Decoder::Create(&group_)); decoder_->set_engine(this); @@ -23,13 +25,15 @@ GPUProcessor::GPUProcessor(CommandBuffer* command_buffer, CommandParser* parser, int commands_per_update) : command_buffer_(command_buffer), - commands_per_update_(commands_per_update) { + commands_per_update_(commands_per_update), + method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { DCHECK(command_buffer); decoder_.reset(decoder); parser_.reset(parser); } GPUProcessor::~GPUProcessor() { + Destroy(); } void GPUProcessor::ProcessCommands() { @@ -59,7 +63,8 @@ void GPUProcessor::ProcessCommands() { if (!parser_->IsEmpty()) { MessageLoop::current()->PostTask( - FROM_HERE, NewRunnableMethod(this, &GPUProcessor::ProcessCommands)); + FROM_HERE, + method_factory_.NewRunnableMethod(&GPUProcessor::ProcessCommands)); } } @@ -83,6 +88,10 @@ int32 GPUProcessor::GetGetOffset() { return parser_->get(); } +void GPUProcessor::ResizeOffscreenFrameBuffer(const gfx::Size& size) { + decoder_->ResizeOffscreenFrameBuffer(size); +} + #if defined(OS_MACOSX) uint64 GPUProcessor::SetWindowSizeForIOSurface(int32 width, int32 height) { return decoder_->SetWindowSizeForIOSurface(width, height); diff --git a/gpu/command_buffer/service/gpu_processor.h b/gpu/command_buffer/service/gpu_processor.h index d34e35d..d61bd60 100644 --- a/gpu/command_buffer/service/gpu_processor.h +++ b/gpu/command_buffer/service/gpu_processor.h @@ -9,7 +9,9 @@ #include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "base/shared_memory.h" +#include "base/task.h" #include "gfx/native_widget_types.h" +#include "gfx/size.h" #include "gpu/command_buffer/common/command_buffer.h" #include "gpu/command_buffer/service/cmd_buffer_engine.h" #include "gpu/command_buffer/service/cmd_parser.h" @@ -20,8 +22,7 @@ namespace gpu { // This class processes commands in a command buffer. It is event driven and // posts tasks to the current message loop to do additional work. -class GPUProcessor : public base::RefCounted<GPUProcessor>, - public CommandBufferEngine { +class GPUProcessor : public CommandBufferEngine { public: explicit GPUProcessor(CommandBuffer* command_buffer); @@ -31,7 +32,10 @@ class GPUProcessor : public base::RefCounted<GPUProcessor>, CommandParser* parser, int commands_per_update); - virtual bool Initialize(gfx::PluginWindowHandle hwnd); + virtual bool Initialize(gfx::PluginWindowHandle hwnd, + GPUProcessor* parent, + const gfx::Size& size, + uint32 parent_texture_id); virtual ~GPUProcessor(); @@ -45,6 +49,9 @@ class GPUProcessor : public base::RefCounted<GPUProcessor>, virtual bool SetGetOffset(int32 offset); virtual int32 GetGetOffset(); + // Asynchronously resizes an offscreen frame buffer. + void ResizeOffscreenFrameBuffer(const gfx::Size& size); + #if defined(OS_MACOSX) // Needed only on Mac OS X, which does not render into an on-screen // window and therefore requires the backing store to be resized @@ -71,37 +78,15 @@ class GPUProcessor : public base::RefCounted<GPUProcessor>, // through the ProcessCommands callback. CommandBuffer* command_buffer_; - scoped_ptr< ::base::SharedMemory> mapped_ring_buffer_; int commands_per_update_; gles2::ContextGroup group_; scoped_ptr<gles2::GLES2Decoder> decoder_; scoped_ptr<CommandParser> parser_; + + ScopedRunnableMethodFactory<GPUProcessor> method_factory_; }; } // namespace gpu -// Callbacks to the GPUProcessor hold a reference count. -template <typename Method> -class CallbackStorage<gpu::GPUProcessor, Method> { - public: - CallbackStorage(gpu::GPUProcessor* obj, Method method) - : obj_(obj), - meth_(method) { - DCHECK(obj_); - obj_->AddRef(); - } - - ~CallbackStorage() { - obj_->Release(); - } - - protected: - gpu::GPUProcessor* obj_; - Method meth_; - - private: - DISALLOW_COPY_AND_ASSIGN(CallbackStorage); -}; - #endif // GPU_COMMAND_BUFFER_SERVICE_GPU_PROCESSOR_H_ diff --git a/gpu/command_buffer/service/gpu_processor_linux.cc b/gpu/command_buffer/service/gpu_processor_linux.cc index 205cceb..d430933 100644 --- a/gpu/command_buffer/service/gpu_processor_linux.cc +++ b/gpu/command_buffer/service/gpu_processor_linux.cc @@ -10,7 +10,10 @@ using ::base::SharedMemory; namespace gpu { -bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) { +bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle, + GPUProcessor* parent, + const gfx::Size& size, + uint32 parent_texture_id) { DCHECK(handle); // Cannot reinitialize. @@ -34,17 +37,27 @@ bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) { // Initialize GAPI immediately if the window handle is valid. XWindowWrapper *window = new XWindowWrapper(GDK_DISPLAY(), handle); decoder_->set_window_wrapper(window); - return decoder_->Initialize(); -} + gles2::GLES2Decoder* parent_decoder = parent ? parent->decoder_.get() : NULL; + if (!decoder_->Initialize(parent_decoder, + size, + parent_texture_id)) { + Destroy(); + return false; + } + + return true;} void GPUProcessor::Destroy() { - // Destroy GAPI if window handle has not already become invalid. - XWindowWrapper *window = decoder_->window(); - if (window) { + // Destroy decoder if initialized. + if (decoder_.get()) { + XWindowWrapper *window = decoder_->window(); decoder_->Destroy(); decoder_->set_window_wrapper(NULL); delete window; + decoder_.reset(); } + + parser_.reset(); } } // namespace gpu diff --git a/gpu/command_buffer/service/gpu_processor_mac.cc b/gpu/command_buffer/service/gpu_processor_mac.cc index a36560c..ef13fc6 100644 --- a/gpu/command_buffer/service/gpu_processor_mac.cc +++ b/gpu/command_buffer/service/gpu_processor_mac.cc @@ -8,7 +8,10 @@ using ::base::SharedMemory; namespace gpu { -bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) { +bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle, + GPUProcessor* parent, + const gfx::Size& size, + uint32 parent_texture_id) { // At this level we do not need the PluginWindowHandle. It is only // needed at the CommandBufferStub level to identify which GPU // plugin instance is creating a new backing store in response to a @@ -29,11 +32,25 @@ bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) { } // Initialize GAPI. - return decoder_->Initialize(); + gles2::GLES2Decoder* parent_decoder = parent ? parent->decoder_.get() : NULL; + if (!decoder_->Initialize(parent_decoder, + size, + parent_texture_id)) { + Destroy(); + return false; + } + + return true; } void GPUProcessor::Destroy() { - decoder_->Destroy(); + // Destroy decoder if initialized. + if (decoder_.get()) { + decoder_->Destroy(); + decoder_.reset(); + } + + parser_.reset(); } } // namespace gpu diff --git a/gpu/command_buffer/service/gpu_processor_unittest.cc b/gpu/command_buffer/service/gpu_processor_unittest.cc index 51cfe01..95be087 100644 --- a/gpu/command_buffer/service/gpu_processor_unittest.cc +++ b/gpu/command_buffer/service/gpu_processor_unittest.cc @@ -58,10 +58,10 @@ class GPUProcessorTest : public testing::Test { 0, async_api_.get()); - processor_ = new GPUProcessor(command_buffer_.get(), - decoder_, - parser_, - 2); + processor_.reset(new GPUProcessor(command_buffer_.get(), + decoder_, + parser_, + 2)); } virtual void TearDown() { @@ -85,7 +85,7 @@ class GPUProcessorTest : public testing::Test { gles2::MockGLES2Decoder* decoder_; CommandParser* parser_; scoped_ptr<AsyncAPIMock> async_api_; - scoped_refptr<GPUProcessor> processor_; + scoped_ptr<GPUProcessor> processor_; }; TEST_F(GPUProcessorTest, ProcessorDoesNothingIfRingBufferIsEmpty) { diff --git a/gpu/command_buffer/service/gpu_processor_win.cc b/gpu/command_buffer/service/gpu_processor_win.cc index bce783b..537872c 100644 --- a/gpu/command_buffer/service/gpu_processor_win.cc +++ b/gpu/command_buffer/service/gpu_processor_win.cc @@ -10,7 +10,10 @@ using ::base::SharedMemory; namespace gpu { -bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) { +bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle, + GPUProcessor* parent, + const gfx::Size& size, + uint32 parent_texture_id) { // Cannot reinitialize. if (parser_.get()) return false; @@ -31,14 +34,25 @@ bool GPUProcessor::Initialize(gfx::PluginWindowHandle handle) { // Initialize GAPI immediately if the window handle is valid. decoder_->set_hwnd(handle); - return decoder_->Initialize(); + gles2::GLES2Decoder* parent_decoder = parent ? parent->decoder_.get() : NULL; + if (!decoder_->Initialize(parent_decoder, + size, + parent_texture_id)) { + Destroy(); + return false; + } + + return true; } void GPUProcessor::Destroy() { // Destroy decoder if initialized. - if (parser_.get()) { + if (decoder_.get()) { decoder_->Destroy(); decoder_->set_hwnd(NULL); + decoder_.reset(); } + + parser_.reset(); } } // namespace gpu diff --git a/gpu/command_buffer/service/texture_manager.cc b/gpu/command_buffer/service/texture_manager.cc index d6a1dce..4369199 100644 --- a/gpu/command_buffer/service/texture_manager.cc +++ b/gpu/command_buffer/service/texture_manager.cc @@ -265,12 +265,13 @@ TextureManager::TextureManager( max_cube_map_texture_size)) { } -void TextureManager::CreateTextureInfo(GLuint texture_id) { +TextureManager::TextureInfo* TextureManager::CreateTextureInfo( + GLuint texture_id) { + TextureInfo::Ref info(new TextureInfo(texture_id)); std::pair<TextureInfoMap::iterator, bool> result = - texture_infos_.insert( - std::make_pair(texture_id, - TextureInfo::Ref(new TextureInfo(texture_id)))); + texture_infos_.insert(std::make_pair(texture_id, info)); DCHECK(result.second); + return info.get(); } TextureManager::TextureInfo* TextureManager::GetTextureInfo( diff --git a/gpu/command_buffer/service/texture_manager.h b/gpu/command_buffer/service/texture_manager.h index 358cbbc..2dba85d 100644 --- a/gpu/command_buffer/service/texture_manager.h +++ b/gpu/command_buffer/service/texture_manager.h @@ -229,7 +229,7 @@ class TextureManager { } // Creates a new texture info. - void CreateTextureInfo(GLuint texture_id); + TextureInfo* CreateTextureInfo(GLuint texture_id); // Gets the texture info for the given texture. TextureInfo* GetTextureInfo(GLuint texture_id); diff --git a/gpu/demos/framework/window.cc b/gpu/demos/framework/window.cc index 600514f..0a9db79 100644 --- a/gpu/demos/framework/window.cc +++ b/gpu/demos/framework/window.cc @@ -50,20 +50,23 @@ void Window::OnPaint() { ::gles2::GetGLContext()->SwapBuffers(); } +// TODO(apatrick): It looks like all the resources allocated here leak. We +// should fix that if we want to use this Window class for anything beyond this +// simple use case. bool Window::CreateRenderContext(gfx::PluginWindowHandle hwnd) { scoped_ptr<CommandBufferService> command_buffer(new CommandBufferService); if (!command_buffer->Initialize(kCommandBufferSize)) { return false; } - scoped_refptr<GPUProcessor> gpu_processor( + GPUProcessor* gpu_processor( new GPUProcessor(command_buffer.get())); - if (!gpu_processor->Initialize(hwnd)) { + if (!gpu_processor->Initialize(hwnd, NULL, gfx::Size(), 0)) { return false; } command_buffer->SetPutOffsetChangeCallback( - NewCallback(gpu_processor.get(), &GPUProcessor::ProcessCommands)); + NewCallback(gpu_processor, &GPUProcessor::ProcessCommands)); GLES2CmdHelper* helper = new GLES2CmdHelper(command_buffer.get()); if (!helper->Initialize()) { diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp index 4e3106f..06818fc 100644 --- a/gpu/gpu.gyp +++ b/gpu/gpu.gyp @@ -247,6 +247,7 @@ 'dependencies': [ 'command_buffer_common', 'gl_libs', + '../gfx/gfx.gyp:gfx', ], 'sources': [ 'command_buffer/service/common_decoder.cc', |