Respond with QuotaExceededError when IndexedDB has no disk space on open.
[chromium-blink-merge.git] / content / common / gpu / client / gpu_channel_host.cc
blob27c71de98a1b28fbb555b72322237bbd4599a72b
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/common/gpu/client/gpu_channel_host.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/debug/trace_event.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/message_loop/message_loop_proxy.h"
13 #include "base/posix/eintr_wrapper.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "content/common/gpu/client/command_buffer_proxy_impl.h"
16 #include "content/common/gpu/client/gpu_video_encode_accelerator_host.h"
17 #include "content/common/gpu/gpu_messages.h"
18 #include "gpu/command_buffer/common/mailbox.h"
19 #include "ipc/ipc_sync_message_filter.h"
20 #include "url/gurl.h"
22 #if defined(OS_WIN)
23 #include "content/public/common/sandbox_init.h"
24 #endif
26 using base::AutoLock;
27 using base::MessageLoopProxy;
29 namespace content {
31 GpuListenerInfo::GpuListenerInfo() {}
33 GpuListenerInfo::~GpuListenerInfo() {}
35 // static
36 scoped_refptr<GpuChannelHost> GpuChannelHost::Create(
37 GpuChannelHostFactory* factory,
38 int gpu_host_id,
39 int client_id,
40 const gpu::GPUInfo& gpu_info,
41 const IPC::ChannelHandle& channel_handle) {
42 DCHECK(factory->IsMainThread());
43 scoped_refptr<GpuChannelHost> host = new GpuChannelHost(
44 factory, gpu_host_id, client_id, gpu_info);
45 host->Connect(channel_handle);
46 return host;
49 GpuChannelHost::GpuChannelHost(GpuChannelHostFactory* factory,
50 int gpu_host_id,
51 int client_id,
52 const gpu::GPUInfo& gpu_info)
53 : factory_(factory),
54 client_id_(client_id),
55 gpu_host_id_(gpu_host_id),
56 gpu_info_(gpu_info) {
57 next_transfer_buffer_id_.GetNext();
60 void GpuChannelHost::Connect(const IPC::ChannelHandle& channel_handle) {
61 // Open a channel to the GPU process. We pass NULL as the main listener here
62 // since we need to filter everything to route it to the right thread.
63 scoped_refptr<base::MessageLoopProxy> io_loop = factory_->GetIOLoopProxy();
64 channel_.reset(new IPC::SyncChannel(channel_handle,
65 IPC::Channel::MODE_CLIENT,
66 NULL,
67 io_loop.get(),
68 true,
69 factory_->GetShutDownEvent()));
71 sync_filter_ = new IPC::SyncMessageFilter(
72 factory_->GetShutDownEvent());
74 channel_->AddFilter(sync_filter_.get());
76 channel_filter_ = new MessageFilter();
78 // Install the filter last, because we intercept all leftover
79 // messages.
80 channel_->AddFilter(channel_filter_.get());
83 bool GpuChannelHost::Send(IPC::Message* msg) {
84 // Callee takes ownership of message, regardless of whether Send is
85 // successful. See IPC::Sender.
86 scoped_ptr<IPC::Message> message(msg);
87 // The GPU process never sends synchronous IPCs so clear the unblock flag to
88 // preserve order.
89 message->set_unblock(false);
91 // Currently we need to choose between two different mechanisms for sending.
92 // On the main thread we use the regular channel Send() method, on another
93 // thread we use SyncMessageFilter. We also have to be careful interpreting
94 // IsMainThread() since it might return false during shutdown,
95 // impl we are actually calling from the main thread (discard message then).
97 // TODO: Can we just always use sync_filter_ since we setup the channel
98 // without a main listener?
99 if (factory_->IsMainThread()) {
100 // http://crbug.com/125264
101 base::ThreadRestrictions::ScopedAllowWait allow_wait;
102 return channel_->Send(message.release());
103 } else if (base::MessageLoop::current()) {
104 return sync_filter_->Send(message.release());
107 return false;
110 CommandBufferProxyImpl* GpuChannelHost::CreateViewCommandBuffer(
111 int32 surface_id,
112 CommandBufferProxyImpl* share_group,
113 const std::vector<int32>& attribs,
114 const GURL& active_url,
115 gfx::GpuPreference gpu_preference) {
116 TRACE_EVENT1("gpu",
117 "GpuChannelHost::CreateViewCommandBuffer",
118 "surface_id",
119 surface_id);
121 GPUCreateCommandBufferConfig init_params;
122 init_params.share_group_id =
123 share_group ? share_group->GetRouteID() : MSG_ROUTING_NONE;
124 init_params.attribs = attribs;
125 init_params.active_url = active_url;
126 init_params.gpu_preference = gpu_preference;
127 int32 route_id = factory_->CreateViewCommandBuffer(surface_id, init_params);
128 if (route_id == MSG_ROUTING_NONE)
129 return NULL;
131 CommandBufferProxyImpl* command_buffer =
132 new CommandBufferProxyImpl(this, route_id);
133 AddRoute(route_id, command_buffer->AsWeakPtr());
135 AutoLock lock(context_lock_);
136 proxies_[route_id] = command_buffer;
137 return command_buffer;
140 CommandBufferProxyImpl* GpuChannelHost::CreateOffscreenCommandBuffer(
141 const gfx::Size& size,
142 CommandBufferProxyImpl* share_group,
143 const std::vector<int32>& attribs,
144 const GURL& active_url,
145 gfx::GpuPreference gpu_preference) {
146 TRACE_EVENT0("gpu", "GpuChannelHost::CreateOffscreenCommandBuffer");
148 GPUCreateCommandBufferConfig init_params;
149 init_params.share_group_id =
150 share_group ? share_group->GetRouteID() : MSG_ROUTING_NONE;
151 init_params.attribs = attribs;
152 init_params.active_url = active_url;
153 init_params.gpu_preference = gpu_preference;
154 int32 route_id;
155 if (!Send(new GpuChannelMsg_CreateOffscreenCommandBuffer(size,
156 init_params,
157 &route_id))) {
158 return NULL;
161 if (route_id == MSG_ROUTING_NONE)
162 return NULL;
164 CommandBufferProxyImpl* command_buffer =
165 new CommandBufferProxyImpl(this, route_id);
166 AddRoute(route_id, command_buffer->AsWeakPtr());
168 AutoLock lock(context_lock_);
169 proxies_[route_id] = command_buffer;
170 return command_buffer;
173 scoped_ptr<media::VideoDecodeAccelerator> GpuChannelHost::CreateVideoDecoder(
174 int command_buffer_route_id,
175 media::VideoCodecProfile profile,
176 media::VideoDecodeAccelerator::Client* client) {
177 AutoLock lock(context_lock_);
178 ProxyMap::iterator it = proxies_.find(command_buffer_route_id);
179 DCHECK(it != proxies_.end());
180 CommandBufferProxyImpl* proxy = it->second;
181 return proxy->CreateVideoDecoder(profile, client).Pass();
184 scoped_ptr<media::VideoEncodeAccelerator> GpuChannelHost::CreateVideoEncoder(
185 media::VideoEncodeAccelerator::Client* client) {
186 TRACE_EVENT0("gpu", "GpuChannelHost::CreateVideoEncoder");
188 scoped_ptr<media::VideoEncodeAccelerator> vea;
189 int32 route_id = MSG_ROUTING_NONE;
190 if (!Send(new GpuChannelMsg_CreateVideoEncoder(&route_id)))
191 return vea.Pass();
192 if (route_id == MSG_ROUTING_NONE)
193 return vea.Pass();
195 vea.reset(new GpuVideoEncodeAcceleratorHost(client, this, route_id));
196 return vea.Pass();
199 void GpuChannelHost::DestroyCommandBuffer(
200 CommandBufferProxyImpl* command_buffer) {
201 TRACE_EVENT0("gpu", "GpuChannelHost::DestroyCommandBuffer");
203 int route_id = command_buffer->GetRouteID();
204 Send(new GpuChannelMsg_DestroyCommandBuffer(route_id));
205 RemoveRoute(route_id);
207 AutoLock lock(context_lock_);
208 proxies_.erase(route_id);
209 delete command_buffer;
212 bool GpuChannelHost::CollectRenderingStatsForSurface(
213 int surface_id, GpuRenderingStats* stats) {
214 TRACE_EVENT0("gpu", "GpuChannelHost::CollectRenderingStats");
216 return Send(new GpuChannelMsg_CollectRenderingStatsForSurface(surface_id,
217 stats));
220 void GpuChannelHost::AddRoute(
221 int route_id, base::WeakPtr<IPC::Listener> listener) {
222 DCHECK(MessageLoopProxy::current().get());
224 scoped_refptr<base::MessageLoopProxy> io_loop = factory_->GetIOLoopProxy();
225 io_loop->PostTask(FROM_HERE,
226 base::Bind(&GpuChannelHost::MessageFilter::AddRoute,
227 channel_filter_.get(), route_id, listener,
228 MessageLoopProxy::current()));
231 void GpuChannelHost::RemoveRoute(int route_id) {
232 scoped_refptr<base::MessageLoopProxy> io_loop = factory_->GetIOLoopProxy();
233 io_loop->PostTask(FROM_HERE,
234 base::Bind(&GpuChannelHost::MessageFilter::RemoveRoute,
235 channel_filter_.get(), route_id));
238 base::SharedMemoryHandle GpuChannelHost::ShareToGpuProcess(
239 base::SharedMemoryHandle source_handle) {
240 if (IsLost())
241 return base::SharedMemory::NULLHandle();
243 #if defined(OS_WIN)
244 // Windows needs to explicitly duplicate the handle out to another process.
245 base::SharedMemoryHandle target_handle;
246 if (!BrokerDuplicateHandle(source_handle,
247 channel_->peer_pid(),
248 &target_handle,
250 DUPLICATE_SAME_ACCESS)) {
251 return base::SharedMemory::NULLHandle();
254 return target_handle;
255 #else
256 int duped_handle = HANDLE_EINTR(dup(source_handle.fd));
257 if (duped_handle < 0)
258 return base::SharedMemory::NULLHandle();
260 return base::FileDescriptor(duped_handle, true);
261 #endif
264 bool GpuChannelHost::GenerateMailboxNames(unsigned num,
265 std::vector<gpu::Mailbox>* names) {
266 DCHECK(names->empty());
267 TRACE_EVENT0("gpu", "GenerateMailboxName");
268 size_t generate_count = channel_filter_->GetMailboxNames(num, names);
270 if (names->size() < num) {
271 std::vector<gpu::Mailbox> new_names;
272 if (!Send(new GpuChannelMsg_GenerateMailboxNames(num - names->size(),
273 &new_names)))
274 return false;
275 names->insert(names->end(), new_names.begin(), new_names.end());
278 if (generate_count > 0)
279 Send(new GpuChannelMsg_GenerateMailboxNamesAsync(generate_count));
281 return true;
284 int32 GpuChannelHost::ReserveTransferBufferId() {
285 return next_transfer_buffer_id_.GetNext();
288 GpuChannelHost::~GpuChannelHost() {
289 // channel_ must be destroyed on the main thread.
290 if (!factory_->IsMainThread())
291 factory_->GetMainLoop()->DeleteSoon(FROM_HERE, channel_.release());
295 GpuChannelHost::MessageFilter::MessageFilter()
296 : lost_(false),
297 requested_mailboxes_(0) {
300 GpuChannelHost::MessageFilter::~MessageFilter() {}
302 void GpuChannelHost::MessageFilter::AddRoute(
303 int route_id,
304 base::WeakPtr<IPC::Listener> listener,
305 scoped_refptr<MessageLoopProxy> loop) {
306 DCHECK(listeners_.find(route_id) == listeners_.end());
307 GpuListenerInfo info;
308 info.listener = listener;
309 info.loop = loop;
310 listeners_[route_id] = info;
313 void GpuChannelHost::MessageFilter::RemoveRoute(int route_id) {
314 ListenerMap::iterator it = listeners_.find(route_id);
315 if (it != listeners_.end())
316 listeners_.erase(it);
319 bool GpuChannelHost::MessageFilter::OnMessageReceived(
320 const IPC::Message& message) {
321 // Never handle sync message replies or we will deadlock here.
322 if (message.is_reply())
323 return false;
325 if (message.routing_id() == MSG_ROUTING_CONTROL)
326 return OnControlMessageReceived(message);
328 ListenerMap::iterator it = listeners_.find(message.routing_id());
330 if (it != listeners_.end()) {
331 const GpuListenerInfo& info = it->second;
332 info.loop->PostTask(
333 FROM_HERE,
334 base::Bind(
335 base::IgnoreResult(&IPC::Listener::OnMessageReceived),
336 info.listener,
337 message));
340 return true;
343 void GpuChannelHost::MessageFilter::OnChannelError() {
344 // Set the lost state before signalling the proxies. That way, if they
345 // themselves post a task to recreate the context, they will not try to re-use
346 // this channel host.
348 AutoLock lock(lock_);
349 lost_ = true;
352 // Inform all the proxies that an error has occurred. This will be reported
353 // via OpenGL as a lost context.
354 for (ListenerMap::iterator it = listeners_.begin();
355 it != listeners_.end();
356 it++) {
357 const GpuListenerInfo& info = it->second;
358 info.loop->PostTask(
359 FROM_HERE,
360 base::Bind(&IPC::Listener::OnChannelError, info.listener));
363 listeners_.clear();
366 bool GpuChannelHost::MessageFilter::IsLost() const {
367 AutoLock lock(lock_);
368 return lost_;
371 size_t GpuChannelHost::MessageFilter::GetMailboxNames(
372 size_t num, std::vector<gpu::Mailbox>* names) {
373 AutoLock lock(lock_);
374 size_t count = std::min(num, mailbox_name_pool_.size());
375 names->insert(names->begin(),
376 mailbox_name_pool_.end() - count,
377 mailbox_name_pool_.end());
378 mailbox_name_pool_.erase(mailbox_name_pool_.end() - count,
379 mailbox_name_pool_.end());
381 const size_t ideal_mailbox_pool_size = 100;
382 size_t total = mailbox_name_pool_.size() + requested_mailboxes_;
383 DCHECK_LE(total, ideal_mailbox_pool_size);
384 if (total >= ideal_mailbox_pool_size / 2)
385 return 0;
386 size_t request = ideal_mailbox_pool_size - total;
387 requested_mailboxes_ += request;
388 return request;
391 bool GpuChannelHost::MessageFilter::OnControlMessageReceived(
392 const IPC::Message& message) {
393 bool handled = true;
395 IPC_BEGIN_MESSAGE_MAP(GpuChannelHost::MessageFilter, message)
396 IPC_MESSAGE_HANDLER(GpuChannelMsg_GenerateMailboxNamesReply,
397 OnGenerateMailboxNamesReply)
398 IPC_MESSAGE_UNHANDLED(handled = false)
399 IPC_END_MESSAGE_MAP()
401 DCHECK(handled);
402 return handled;
405 void GpuChannelHost::MessageFilter::OnGenerateMailboxNamesReply(
406 const std::vector<gpu::Mailbox>& names) {
407 TRACE_EVENT0("gpu", "OnGenerateMailboxNamesReply");
408 AutoLock lock(lock_);
409 DCHECK_LE(names.size(), requested_mailboxes_);
410 requested_mailboxes_ -= names.size();
411 mailbox_name_pool_.insert(mailbox_name_pool_.end(),
412 names.begin(),
413 names.end());
417 } // namespace content