OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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. | |
4 | |
5 #include "content/renderer/renderer_gl_context.h" | |
6 | |
7 #include "base/debug/trace_event.h" | |
8 #include "base/lazy_instance.h" | |
9 #include "base/memory/ref_counted.h" | |
10 #include "base/memory/scoped_ptr.h" | |
11 #include "base/memory/singleton.h" | |
12 #include "base/memory/weak_ptr.h" | |
13 #include "base/shared_memory.h" | |
14 #include "content/common/view_messages.h" | |
15 #include "content/renderer/command_buffer_proxy.h" | |
16 #include "content/renderer/gpu_channel_host.h" | |
17 #include "content/renderer/gpu_video_service_host.h" | |
18 #include "content/renderer/media/gles2_video_decode_context.h" | |
19 #include "content/renderer/render_thread.h" | |
20 #include "content/renderer/render_widget.h" | |
21 #include "content/renderer/transport_texture_host.h" | |
22 #include "content/renderer/transport_texture_service.h" | |
23 #include "googleurl/src/gurl.h" | |
24 #include "ipc/ipc_channel_handle.h" | |
25 | |
26 #if defined(ENABLE_GPU) | |
27 #include "gpu/command_buffer/client/gles2_cmd_helper.h" | |
28 #include "gpu/command_buffer/client/gles2_implementation.h" | |
29 #include "gpu/command_buffer/client/gles2_lib.h" | |
30 #include "gpu/command_buffer/common/constants.h" | |
31 #include "gpu/GLES2/gles2_command_buffer.h" | |
32 #endif // ENABLE_GPU | |
33 | |
34 namespace { | |
35 | |
36 const int32 kCommandBufferSize = 1024 * 1024; | |
37 // TODO(kbr): make the transfer buffer size configurable via context | |
38 // creation attributes. | |
39 const int32 kTransferBufferSize = 1024 * 1024; | |
40 | |
41 const uint32 kMaxLatchesPerRenderer = 2048; | |
42 const uint32 kInvalidLatchId = 0xffffffffu; | |
43 | |
44 // Singleton used to initialize and terminate the gles2 library. | |
45 class GLES2Initializer { | |
46 public: | |
47 GLES2Initializer() { | |
48 gles2::Initialize(); | |
49 } | |
50 | |
51 ~GLES2Initializer() { | |
52 gles2::Terminate(); | |
53 } | |
54 | |
55 private: | |
56 DISALLOW_COPY_AND_ASSIGN(GLES2Initializer); | |
57 }; | |
58 | |
59 // Shared memory allocator for latches. Creates a block of shared memory for | |
60 // each renderer process. | |
61 class LatchAllocator { | |
62 public: | |
63 static LatchAllocator* GetInstance(); | |
64 static uint32 size() { return kMaxLatchesPerRenderer*sizeof(uint32); } | |
65 static const uint32_t kFreeLatch = 0xffffffffu; | |
66 | |
67 LatchAllocator(); | |
68 ~LatchAllocator(); | |
69 | |
70 base::SharedMemoryHandle handle() const { return shm_->handle(); } | |
71 base::SharedMemory* shared_memory() { return shm_.get(); } | |
72 | |
73 bool AllocateLatch(uint32* latch_id); | |
74 bool FreeLatch(uint32 latch_id); | |
75 | |
76 private: | |
77 friend struct DefaultSingletonTraits<LatchAllocator>; | |
78 | |
79 scoped_ptr<base::SharedMemory> shm_; | |
80 // Pointer to mapped shared memory. | |
81 volatile uint32* latches_; | |
82 | |
83 DISALLOW_COPY_AND_ASSIGN(LatchAllocator); | |
84 }; | |
85 | |
86 //////////////////////////////////////////////////////////////////////////////// | |
87 /// LatchAllocator implementation | |
88 | |
89 LatchAllocator* LatchAllocator::GetInstance() { | |
90 return Singleton<LatchAllocator>::get(); | |
91 } | |
92 | |
93 LatchAllocator::LatchAllocator() { | |
94 base::SharedMemoryHandle handle; | |
95 RenderThread* render_thread = RenderThread::current(); | |
96 if (!render_thread->Send( | |
97 new ViewHostMsg_AllocateSharedMemoryBuffer(size(), &handle))) { | |
98 NOTREACHED() << "failed to send sync IPC"; | |
99 } | |
100 | |
101 if (!base::SharedMemory::IsHandleValid(handle)) { | |
102 NOTREACHED() << "failed to create shared memory"; | |
103 } | |
104 | |
105 // Handle is closed by the SharedMemory object below. This stops | |
106 // base::FileDescriptor from closing it as well. | |
107 #if defined(OS_POSIX) | |
108 handle.auto_close = false; | |
109 #endif | |
110 | |
111 shm_.reset(new base::SharedMemory(handle, false)); | |
112 if (!shm_->Map(size())) { | |
113 NOTREACHED() << "failed to map shared memory"; | |
114 } | |
115 | |
116 latches_ = static_cast<uint32*>(shm_->memory()); | |
117 // Mark all latches as unallocated. | |
118 for (uint32 i = 0; i < kMaxLatchesPerRenderer; ++i) | |
119 latches_[i] = kFreeLatch; | |
120 } | |
121 | |
122 LatchAllocator::~LatchAllocator() { | |
123 } | |
124 | |
125 bool LatchAllocator::AllocateLatch(uint32* latch_id) { | |
126 for (uint32 i = 0; i < kMaxLatchesPerRenderer; ++i) { | |
127 if (latches_[i] == kFreeLatch) { | |
128 // mark latch as taken and blocked. | |
129 // 0 means waiter will block, 1 means waiter will pass. | |
130 latches_[i] = 0; | |
131 *latch_id = i; | |
132 return true; | |
133 } | |
134 } | |
135 return false; | |
136 } | |
137 | |
138 bool LatchAllocator::FreeLatch(uint32 latch_id) { | |
139 if (latch_id < kMaxLatchesPerRenderer && latches_[latch_id] != kFreeLatch) { | |
140 latches_[latch_id] = kFreeLatch; | |
141 return true; | |
142 } | |
143 return false; | |
144 } | |
145 | |
146 //////////////////////////////////////////////////////////////////////////////// | |
147 | |
148 static base::LazyInstance<GLES2Initializer> g_gles2_initializer( | |
149 base::LINKER_INITIALIZED); | |
150 | |
151 } // namespace anonymous | |
152 | |
153 RendererGLContext::~RendererGLContext() { | |
154 Destroy(); | |
155 } | |
156 | |
157 RendererGLContext* RendererGLContext::CreateViewContext( | |
158 GpuChannelHost* channel, | |
159 gfx::PluginWindowHandle render_surface, | |
160 int render_view_id, | |
161 const char* allowed_extensions, | |
162 const int32* attrib_list, | |
163 const GURL& active_url) { | |
164 #if defined(ENABLE_GPU) | |
165 scoped_ptr<RendererGLContext> context(new RendererGLContext(channel, NULL)); | |
166 if (!context->Initialize( | |
167 true, | |
168 render_surface, | |
169 render_view_id, | |
170 gfx::Size(), | |
171 allowed_extensions, | |
172 attrib_list, | |
173 active_url)) | |
174 return NULL; | |
175 | |
176 return context.release(); | |
177 #else | |
178 return NULL; | |
179 #endif | |
180 } | |
181 | |
182 #if defined(OS_MACOSX) | |
183 void RendererGLContext::ResizeOnscreen(const gfx::Size& size) { | |
184 DCHECK(size.width() > 0 && size.height() > 0); | |
185 size_ = size; | |
186 command_buffer_->SetWindowSize(size); | |
187 } | |
188 #endif | |
189 | |
190 RendererGLContext* RendererGLContext::CreateOffscreenContext( | |
191 GpuChannelHost* channel, | |
192 RendererGLContext* parent, | |
193 const gfx::Size& size, | |
194 const char* allowed_extensions, | |
195 const int32* attrib_list, | |
196 const GURL& active_url) { | |
197 #if defined(ENABLE_GPU) | |
198 scoped_ptr<RendererGLContext> context(new RendererGLContext(channel, parent)); | |
199 if (!context->Initialize( | |
200 false, | |
201 gfx::kNullPluginWindow, | |
202 0, | |
203 size, | |
204 allowed_extensions, | |
205 attrib_list, | |
206 active_url)) | |
207 return NULL; | |
208 | |
209 return context.release(); | |
210 #else | |
211 return NULL; | |
212 #endif | |
213 } | |
214 | |
215 void RendererGLContext::ResizeOffscreen(const gfx::Size& size) { | |
216 DCHECK(size.width() > 0 && size.height() > 0); | |
217 if (size_ != size) { | |
218 command_buffer_->ResizeOffscreenFrameBuffer(size); | |
219 size_ = size; | |
220 } | |
221 } | |
222 | |
223 uint32 RendererGLContext::GetParentTextureId() { | |
224 return parent_texture_id_; | |
225 } | |
226 | |
227 uint32 RendererGLContext::CreateParentTexture(const gfx::Size& size) { | |
228 // Allocate a texture ID with respect to the parent. | |
229 if (parent_.get()) { | |
230 if (!MakeCurrent(parent_.get())) | |
231 return 0; | |
232 uint32 texture_id = parent_->gles2_implementation_->MakeTextureId(); | |
233 parent_->gles2_implementation_->BindTexture(GL_TEXTURE_2D, texture_id); | |
234 parent_->gles2_implementation_->TexParameteri( | |
235 GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
236 parent_->gles2_implementation_->TexParameteri( | |
237 GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
238 parent_->gles2_implementation_->TexParameteri( | |
239 GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | |
240 parent_->gles2_implementation_->TexParameteri( | |
241 GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | |
242 | |
243 parent_->gles2_implementation_->TexImage2D(GL_TEXTURE_2D, | |
244 0, // mip level | |
245 GL_RGBA, | |
246 size.width(), | |
247 size.height(), | |
248 0, // border | |
249 GL_RGBA, | |
250 GL_UNSIGNED_BYTE, | |
251 NULL); | |
252 // Make sure that the parent texture's storage is allocated before we let | |
253 // the caller attempt to use it. | |
254 int32 token = parent_->gles2_helper_->InsertToken(); | |
255 parent_->gles2_helper_->WaitForToken(token); | |
256 return texture_id; | |
257 } | |
258 return 0; | |
259 } | |
260 | |
261 void RendererGLContext::DeleteParentTexture(uint32 texture) { | |
262 if (parent_.get()) { | |
263 if (!MakeCurrent(parent_.get())) | |
264 return; | |
265 parent_->gles2_implementation_->DeleteTextures(1, &texture); | |
266 } | |
267 } | |
268 | |
269 void RendererGLContext::SetSwapBuffersCallback(Callback0::Type* callback) { | |
270 swap_buffers_callback_.reset(callback); | |
271 } | |
272 | |
273 void RendererGLContext::SetContextLostCallback(Callback0::Type* callback) { | |
274 context_lost_callback_.reset(callback); | |
275 } | |
276 | |
277 bool RendererGLContext::MakeCurrent(RendererGLContext* context) { | |
278 if (context) { | |
279 gles2::SetGLContext(context->gles2_implementation_); | |
280 | |
281 // Don't request latest error status from service. Just use the locally | |
282 // cached information from the last flush. | |
283 // TODO(apatrick): I'm not sure if this should actually change the | |
284 // current context if it fails. For now it gets changed even if it fails | |
285 // because making GL calls with a NULL context crashes. | |
286 if (context->command_buffer_->GetLastState().error != gpu::error::kNoError) | |
287 return false; | |
288 } else { | |
289 gles2::SetGLContext(NULL); | |
290 } | |
291 | |
292 return true; | |
293 } | |
294 | |
295 bool RendererGLContext::SwapBuffers() { | |
296 TRACE_EVENT1("gpu", "RendererGLContext::SwapBuffers", "frame", frame_number_); | |
297 frame_number_++; | |
298 | |
299 // Don't request latest error status from service. Just use the locally cached | |
300 // information from the last flush. | |
301 if (command_buffer_->GetLastState().error != gpu::error::kNoError) | |
302 return false; | |
303 | |
304 gles2_implementation_->SwapBuffers(); | |
305 return true; | |
306 } | |
307 | |
308 media::VideoDecodeEngine* RendererGLContext::CreateVideoDecodeEngine() { | |
309 return channel_->gpu_video_service_host()->CreateVideoDecoder( | |
310 command_buffer_->route_id()); | |
311 } | |
312 | |
313 media::VideoDecodeContext* RendererGLContext::CreateVideoDecodeContext( | |
314 MessageLoop* message_loop, bool hardware_decoder) { | |
315 return new Gles2VideoDecodeContext(message_loop, hardware_decoder, this); | |
316 } | |
317 | |
318 scoped_refptr<TransportTextureHost> | |
319 RendererGLContext::CreateTransportTextureHost() { | |
320 return channel_->transport_texture_service()->CreateTransportTextureHost( | |
321 this, command_buffer_->route_id()); | |
322 } | |
323 | |
324 RendererGLContext::Error RendererGLContext::GetError() { | |
325 gpu::CommandBuffer::State state = command_buffer_->GetState(); | |
326 if (state.error == gpu::error::kNoError) { | |
327 Error old_error = last_error_; | |
328 last_error_ = SUCCESS; | |
329 return old_error; | |
330 } else { | |
331 // All command buffer errors are unrecoverable. The error is treated as a | |
332 // lost context: destroy the context and create another one. | |
333 return CONTEXT_LOST; | |
334 } | |
335 } | |
336 | |
337 bool RendererGLContext::IsCommandBufferContextLost() { | |
338 gpu::CommandBuffer::State state = command_buffer_->GetLastState(); | |
339 return state.error == gpu::error::kLostContext; | |
340 } | |
341 | |
342 CommandBufferProxy* RendererGLContext::GetCommandBufferProxy() { | |
343 return command_buffer_; | |
344 } | |
345 | |
346 // TODO(gman): Remove This | |
347 void RendererGLContext::DisableShaderTranslation() { | |
348 gles2_implementation_->CommandBufferEnableCHROMIUM( | |
349 PEPPER3D_SKIP_GLSL_TRANSLATION); | |
350 } | |
351 | |
352 gpu::gles2::GLES2Implementation* RendererGLContext::GetImplementation() { | |
353 return gles2_implementation_; | |
354 } | |
355 | |
356 RendererGLContext::RendererGLContext(GpuChannelHost* channel, | |
357 RendererGLContext* parent) | |
358 : channel_(channel), | |
359 parent_(parent ? | |
360 parent->AsWeakPtr() : base::WeakPtr<RendererGLContext>()), | |
361 parent_texture_id_(0), | |
362 child_to_parent_latch_(kInvalidLatchId), | |
363 parent_to_child_latch_(kInvalidLatchId), | |
364 latch_transfer_buffer_id_(-1), | |
365 command_buffer_(NULL), | |
366 gles2_helper_(NULL), | |
367 transfer_buffer_id_(-1), | |
368 gles2_implementation_(NULL), | |
369 last_error_(SUCCESS), | |
370 frame_number_(0) { | |
371 DCHECK(channel); | |
372 } | |
373 | |
374 bool RendererGLContext::Initialize(bool onscreen, | |
375 gfx::PluginWindowHandle render_surface, | |
376 int render_view_id, | |
377 const gfx::Size& size, | |
378 const char* allowed_extensions, | |
379 const int32* attrib_list, | |
380 const GURL& active_url) { | |
381 DCHECK(size.width() >= 0 && size.height() >= 0); | |
382 TRACE_EVENT2("gpu", "RendererGLContext::Initialize", | |
383 "on_screen", onscreen, "num_pixels", size.GetArea()); | |
384 | |
385 if (channel_->state() != GpuChannelHost::kConnected) | |
386 return false; | |
387 | |
388 // Ensure the gles2 library is initialized first in a thread safe way. | |
389 g_gles2_initializer.Get(); | |
390 | |
391 // Allocate a frame buffer ID with respect to the parent. | |
392 if (parent_.get()) { | |
393 // Flush any remaining commands in the parent context to make sure the | |
394 // texture id accounting stays consistent. | |
395 int32 token = parent_->gles2_helper_->InsertToken(); | |
396 parent_->gles2_helper_->WaitForToken(token); | |
397 parent_texture_id_ = parent_->gles2_implementation_->MakeTextureId(); | |
398 } | |
399 | |
400 std::vector<int32> attribs; | |
401 while (attrib_list) { | |
402 int32 attrib = *attrib_list++; | |
403 switch (attrib) { | |
404 // Known attributes | |
405 case ALPHA_SIZE: | |
406 case BLUE_SIZE: | |
407 case GREEN_SIZE: | |
408 case RED_SIZE: | |
409 case DEPTH_SIZE: | |
410 case STENCIL_SIZE: | |
411 case SAMPLES: | |
412 case SAMPLE_BUFFERS: | |
413 attribs.push_back(attrib); | |
414 attribs.push_back(*attrib_list++); | |
415 break; | |
416 case NONE: | |
417 attribs.push_back(attrib); | |
418 attrib_list = NULL; | |
419 break; | |
420 default: | |
421 last_error_ = BAD_ATTRIBUTE; | |
422 attribs.push_back(NONE); | |
423 attrib_list = NULL; | |
424 break; | |
425 } | |
426 } | |
427 | |
428 // Create a proxy to a command buffer in the GPU process. | |
429 if (onscreen) { | |
430 if (render_surface == gfx::kNullPluginWindow) { | |
431 LOG(ERROR) << "Invalid surface handle for onscreen context."; | |
432 command_buffer_ = NULL; | |
433 } else { | |
434 command_buffer_ = channel_->CreateViewCommandBuffer( | |
435 render_surface, | |
436 render_view_id, | |
437 allowed_extensions, | |
438 attribs, | |
439 active_url); | |
440 } | |
441 } else { | |
442 CommandBufferProxy* parent_command_buffer = | |
443 parent_.get() ? parent_->command_buffer_ : NULL; | |
444 command_buffer_ = channel_->CreateOffscreenCommandBuffer( | |
445 parent_command_buffer, | |
446 size, | |
447 allowed_extensions, | |
448 attribs, | |
449 parent_texture_id_, | |
450 active_url); | |
451 } | |
452 if (!command_buffer_) { | |
453 Destroy(); | |
454 return false; | |
455 } | |
456 | |
457 // Initiaize the command buffer. | |
458 if (!command_buffer_->Initialize(kCommandBufferSize)) { | |
459 Destroy(); | |
460 return false; | |
461 } | |
462 | |
463 command_buffer_->SetSwapBuffersCallback( | |
464 NewCallback(this, &RendererGLContext::OnSwapBuffers)); | |
465 | |
466 command_buffer_->SetChannelErrorCallback( | |
467 NewCallback(this, &RendererGLContext::OnContextLost)); | |
468 | |
469 // Create the GLES2 helper, which writes the command buffer protocol. | |
470 gles2_helper_ = new gpu::gles2::GLES2CmdHelper(command_buffer_); | |
471 if (!gles2_helper_->Initialize(kCommandBufferSize)) { | |
472 Destroy(); | |
473 return false; | |
474 } | |
475 | |
476 // Create a transfer buffer used to copy resources between the renderer | |
477 // process and the GPU process. | |
478 transfer_buffer_id_ = | |
479 command_buffer_->CreateTransferBuffer(kTransferBufferSize, | |
480 gpu::kCommandBufferSharedMemoryId); | |
481 if (transfer_buffer_id_ < 0) { | |
482 Destroy(); | |
483 return false; | |
484 } | |
485 | |
486 // Map the buffer into the renderer process's address space. | |
487 gpu::Buffer transfer_buffer = | |
488 command_buffer_->GetTransferBuffer(transfer_buffer_id_); | |
489 if (!transfer_buffer.ptr) { | |
490 Destroy(); | |
491 return false; | |
492 } | |
493 | |
494 // Register transfer buffer so that the context can access latches. | |
495 LatchAllocator* latch_shm = LatchAllocator::GetInstance(); | |
496 latch_transfer_buffer_id_ = command_buffer_->RegisterTransferBuffer( | |
497 latch_shm->shared_memory(), LatchAllocator::size(), | |
498 gpu::kLatchSharedMemoryId); | |
499 if (latch_transfer_buffer_id_ != gpu::kLatchSharedMemoryId) { | |
500 Destroy(); | |
501 return false; | |
502 } | |
503 | |
504 // If this is a child context, setup latches for synchronization between child | |
505 // and parent. | |
506 if (parent_.get()) { | |
507 if (!CreateLatch(&child_to_parent_latch_) || | |
508 !CreateLatch(&parent_to_child_latch_)) { | |
509 Destroy(); | |
510 return false; | |
511 } | |
512 } | |
513 | |
514 // Create the object exposing the OpenGL API. | |
515 gles2_implementation_ = new gpu::gles2::GLES2Implementation( | |
516 gles2_helper_, | |
517 transfer_buffer.size, | |
518 transfer_buffer.ptr, | |
519 transfer_buffer_id_, | |
520 false); | |
521 | |
522 size_ = size; | |
523 | |
524 return true; | |
525 } | |
526 | |
527 void RendererGLContext::Destroy() { | |
528 if (parent_.get() && parent_texture_id_ != 0) { | |
529 parent_->gles2_implementation_->FreeTextureId(parent_texture_id_); | |
530 parent_texture_id_ = 0; | |
531 } | |
532 | |
533 delete gles2_implementation_; | |
534 gles2_implementation_ = NULL; | |
535 | |
536 if (child_to_parent_latch_ != kInvalidLatchId) { | |
537 DestroyLatch(child_to_parent_latch_); | |
538 child_to_parent_latch_ = kInvalidLatchId; | |
539 } | |
540 if (parent_to_child_latch_ != kInvalidLatchId) { | |
541 DestroyLatch(parent_to_child_latch_); | |
542 parent_to_child_latch_ = kInvalidLatchId; | |
543 } | |
544 if (command_buffer_ && latch_transfer_buffer_id_ != -1) { | |
545 command_buffer_->DestroyTransferBuffer(latch_transfer_buffer_id_); | |
546 latch_transfer_buffer_id_ = -1; | |
547 } | |
548 | |
549 if (command_buffer_ && transfer_buffer_id_ != -1) { | |
550 command_buffer_->DestroyTransferBuffer(transfer_buffer_id_); | |
551 transfer_buffer_id_ = -1; | |
552 } | |
553 | |
554 delete gles2_helper_; | |
555 gles2_helper_ = NULL; | |
556 | |
557 if (channel_ && command_buffer_) { | |
558 channel_->DestroyCommandBuffer(command_buffer_); | |
559 command_buffer_ = NULL; | |
560 } | |
561 | |
562 channel_ = NULL; | |
563 } | |
564 | |
565 void RendererGLContext::OnSwapBuffers() { | |
566 if (swap_buffers_callback_.get()) | |
567 swap_buffers_callback_->Run(); | |
568 } | |
569 | |
570 void RendererGLContext::OnContextLost() { | |
571 if (context_lost_callback_.get()) | |
572 context_lost_callback_->Run(); | |
573 } | |
574 | |
575 bool RendererGLContext::CreateLatch(uint32* ret_latch) { | |
576 return LatchAllocator::GetInstance()->AllocateLatch(ret_latch); | |
577 } | |
578 | |
579 bool RendererGLContext::DestroyLatch(uint32 latch) { | |
580 return LatchAllocator::GetInstance()->FreeLatch(latch); | |
581 } | |
582 | |
583 bool RendererGLContext::GetParentToChildLatch(uint32* parent_to_child_latch) { | |
584 if (parent_.get()) { | |
585 *parent_to_child_latch = parent_to_child_latch_; | |
586 return true; | |
587 } | |
588 return false; | |
589 } | |
590 | |
591 bool RendererGLContext::GetChildToParentLatch(uint32* child_to_parent_latch) { | |
592 if (parent_.get()) { | |
593 *child_to_parent_latch = child_to_parent_latch_; | |
594 return true; | |
595 } | |
596 return false; | |
597 } | |
OLD | NEW |