Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(146)

Side by Side Diff: gpu/command_buffer/service/in_process_command_buffer.cc

Issue 19522006: GLInProcessContext: support async flushes and dedicated GPU thread (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include "gpu/command_buffer/service/in_process_command_buffer.h"
6
7 #include <queue>
8 #include <utility>
9
10 #include <GLES2/gl2.h>
11 #ifndef GL_GLEXT_PROTOTYPES
12 #define GL_GLEXT_PROTOTYPES 1
13 #endif
14 #include <GLES2/gl2ext.h>
15 #include <GLES2/gl2extchromium.h>
16
17 #include "base/bind.h"
18 #include "base/bind_helpers.h"
19 #include "base/lazy_instance.h"
20 #include "base/logging.h"
21 #include "base/memory/scoped_ptr.h"
22 #include "base/memory/weak_ptr.h"
23 #include "base/message_loop/message_loop.h"
24 #include "base/message_loop/message_loop_proxy.h"
25 #include "base/threading/thread.h"
26 #include "gpu/command_buffer/client/share_group.h"
27 #include "gpu/command_buffer/common/id_allocator.h"
28 #include "gpu/command_buffer/service/command_buffer_service.h"
29 #include "gpu/command_buffer/service/context_group.h"
30 #include "gpu/command_buffer/service/gl_context_virtual.h"
31 #include "gpu/command_buffer/service/gpu_scheduler.h"
32 #include "gpu/command_buffer/service/image_manager.h"
33 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
34 #include "ui/gfx/size.h"
35 #include "ui/gl/gl_context.h"
36 #include "ui/gl/gl_image.h"
37 #include "ui/gl/gl_share_group.h"
38 #include "ui/gl/gl_surface.h"
39
40 namespace gpu {
41
42 namespace {
43
44 static base::LazyInstance<std::set<InProcessCommandBuffer*> >
45 g_all_shared_contexts = LAZY_INSTANCE_INITIALIZER;
46
47 static bool g_use_virtualized_gl_context = false;
48
49 template <typename T>
50 static void RunTaskWithResult(base::Callback<T(void)> task,
51 T* result,
52 base::WaitableEvent* completion) {
53 *result = task.Run();
54 completion->Signal();
55 }
56
57 class GpuInProcessThread : public base::Thread {
58 public:
59 GpuInProcessThread();
60 virtual ~GpuInProcessThread();
61
62 private:
63 DISALLOW_COPY_AND_ASSIGN(GpuInProcessThread);
64 };
65
66 GpuInProcessThread::GpuInProcessThread() : base::Thread("GpuThread") {
67 Start();
68 }
69
70 GpuInProcessThread::~GpuInProcessThread() {
71 Stop();
72 }
73
74 class GpuCommandQueue {
75 public:
76 GpuCommandQueue();
77 virtual ~GpuCommandQueue();
78
79 void QueueTask(const base::Closure& task);
80 void RunTasks();
81 void SetScheduleCallback(const base::Closure& callback);
82
83 private:
84 base::Lock tasks_lock_;
85 std::queue<base::Closure> tasks_;
86
87 base::Closure schedule_callback_;
88 base::LazyInstance<GpuInProcessThread>::Leaky thread_;
89
90 DISALLOW_COPY_AND_ASSIGN(GpuCommandQueue);
91 };
92
93 GpuCommandQueue::GpuCommandQueue() {}
94
95 GpuCommandQueue::~GpuCommandQueue() {
96 base::AutoLock lock(tasks_lock_);
97 DCHECK(tasks_.empty());
98 }
99
100 void GpuCommandQueue::QueueTask(const base::Closure& task) {
101 {
102 base::AutoLock lock(tasks_lock_);
103 tasks_.push(task);
104 }
105
106 if (!schedule_callback_.is_null()) {
107 schedule_callback_.Run();
108 return;
109 }
110 thread_.Get().message_loop()
111 ->PostTask(FROM_HERE,
112 base::Bind(&GpuCommandQueue::RunTasks,
113 base::Unretained(this)));
114 }
115
116 void GpuCommandQueue::RunTasks() {
117 size_t num_tasks;
118 {
119 base::AutoLock lock(tasks_lock_);
120 num_tasks = tasks_.size();
121 }
122
123 while (num_tasks) {
124 base::Closure task;
125 {
126 base::AutoLock lock(tasks_lock_);
127 task = tasks_.front();
128 tasks_.pop();
129 num_tasks = tasks_.size();
130 }
131
132 task.Run();
133 }
134 }
135
136 void GpuCommandQueue::SetScheduleCallback(const base::Closure& callback) {
137 DCHECK(schedule_callback_.is_null());
138 schedule_callback_ = callback;
139 }
140
141 static base::LazyInstance<GpuCommandQueue>::Leaky g_gpu_queue =
142 LAZY_INSTANCE_INITIALIZER;
143
144 static void QueueTask(const base::Closure& task) {
145 g_gpu_queue.Get().QueueTask(task);
146 }
147
148 class ScopedEvent {
149 public:
150 ScopedEvent(base::WaitableEvent* event) : event_(event) {}
151 ~ScopedEvent() { event_->Signal(); }
152
153 private:
154 base::WaitableEvent* event_;
155 };
156
157 } // anonyous namespace
158
159 InProcessCommandBuffer::InProcessCommandBuffer()
160 : context_lost_(false),
161 last_put_offset_(-1),
162 flush_event_(false, false) {}
163
164 InProcessCommandBuffer::~InProcessCommandBuffer() {
165 Destroy();
166 }
167
168 bool InProcessCommandBuffer::IsContextLost() {
169 if (context_lost_ || !command_buffer_) {
170 return true;
171 }
172 CommandBuffer::State state = GetState();
173 return error::IsError(state.error);
174 }
175
176 void InProcessCommandBuffer::OnResizeView(gfx::Size size, float scale_factor) {
177 DCHECK(!surface_->IsOffscreen());
178 surface_->Resize(size);
179 }
180
181 bool InProcessCommandBuffer::MakeCurrent() {
182 command_buffer_lock_.AssertAcquired();
183
184 if (decoder_->MakeCurrent())
185 return true;
186 DLOG(ERROR) << "Context lost because MakeCurrent failed.";
187 command_buffer_->SetContextLostReason(decoder_->GetContextLostReason());
188 command_buffer_->SetParseError(gpu::error::kLostContext);
189 return false;
190 }
191
192 void InProcessCommandBuffer::PumpCommands() {
193 ScopedEvent handle_flush(&flush_event_);
194 command_buffer_lock_.AssertAcquired();
195
196 if (!MakeCurrent())
197 return;
198
199 gpu_scheduler_->PutChanged();
200 CommandBuffer::State state = command_buffer_->GetState();
201 DCHECK((!error::IsError(state.error) && !context_lost_) ||
202 (error::IsError(state.error) && context_lost_));
203 }
204
205 bool InProcessCommandBuffer::GetBufferChanged(int32 transfer_buffer_id) {
206 command_buffer_lock_.AssertAcquired();
207 command_buffer_->SetGetBuffer(transfer_buffer_id);
208 return true;
209 }
210
211 bool InProcessCommandBuffer::Initialize(
212 bool is_offscreen,
213 bool share_resources,
214 gfx::AcceleratedWidget window,
215 const gfx::Size& size,
216 const char* allowed_extensions,
217 const std::vector<int32>& attribs,
218 gfx::GpuPreference gpu_preference,
219 const base::Closure& context_lost_callback,
220 scoped_refptr<gles2::ShareGroup>* client_share_group) {
221
222 share_resources_ = share_resources;
223 context_lost_callback_ = WrapCallback(context_lost_callback);
224
225 base::WaitableEvent completion(true, false);
226 bool result;
227 base::Callback<bool(void)> init_task =
228 base::Bind(&InProcessCommandBuffer::InitializeOnGpuThread,
229 base::Unretained(this),
230 is_offscreen,
231 window,
232 size,
233 allowed_extensions,
234 attribs,
235 gpu_preference);
236 QueueTask(
237 base::Bind(&RunTaskWithResult<bool>, init_task, &result, &completion));
238 completion.Wait();
239 *client_share_group = client_share_group_;
240 return result;
241 }
242
243 bool InProcessCommandBuffer::InitializeOnGpuThread(
244 bool is_offscreen,
245 gfx::AcceleratedWidget window,
246 const gfx::Size& size,
247 const char* allowed_extensions,
248 const std::vector<int32>& attribs,
249 gfx::GpuPreference gpu_preference) {
250 // Use one share group for all contexts.
251 CR_DEFINE_STATIC_LOCAL(scoped_refptr<gfx::GLShareGroup>, share_group,
252 (new gfx::GLShareGroup));
253 CR_DEFINE_STATIC_LOCAL(scoped_refptr<gles2::ShareGroup>, client_share_group,
254 (new gles2::ShareGroup(true, false)));
255
256 DCHECK(size.width() >= 0 && size.height() >= 0);
257
258 TransferBufferManager* manager = new TransferBufferManager();
259 transfer_buffer_manager_.reset(manager);
260 manager->Initialize();
261
262 scoped_ptr<CommandBufferService> command_buffer(
263 new CommandBufferService(transfer_buffer_manager_.get()));
264 command_buffer->SetPutOffsetChangeCallback(base::Bind(
265 &InProcessCommandBuffer::PumpCommands, base::Unretained(this)));
266 command_buffer->SetParseErrorCallback(base::Bind(
267 &InProcessCommandBuffer::OnContextLost, base::Unretained(this)));
268
269 if (!command_buffer->Initialize()) {
270 LOG(ERROR) << "Could not initialize command buffer.";
271 Destroy();
272 return false;
273 }
274
275 InProcessCommandBuffer* context_group = NULL;
276
277 if (share_resources_ && !g_all_shared_contexts.Get().empty()) {
278 for (std::set<InProcessCommandBuffer*>::iterator it =
279 g_all_shared_contexts.Get().begin();
280 it != g_all_shared_contexts.Get().end();
281 ++it) {
282 if (!(*it)->IsContextLost()) {
283 context_group = *it;
284 break;
285 }
286 }
287 if (!context_group) {
288 share_group = new gfx::GLShareGroup;
289 client_share_group = new gles2::ShareGroup(true, false);
290 }
291 }
292
293 if (share_resources_)
294 client_share_group_ = client_share_group;
295
296 // TODO(gman): This needs to be true if this is Pepper.
297 bool bind_generates_resource = false;
298 decoder_.reset(gles2::GLES2Decoder::Create(
299 context_group ? context_group->decoder_->GetContextGroup()
300 : new gles2::ContextGroup(
301 NULL, NULL, NULL, NULL, bind_generates_resource)));
302
303 gpu_scheduler_.reset(
304 new GpuScheduler(command_buffer.get(), decoder_.get(), decoder_.get()));
305 command_buffer->SetGetBufferChangeCallback(base::Bind(
306 &GpuScheduler::SetGetBuffer, base::Unretained(gpu_scheduler_.get())));
307 command_buffer_ = command_buffer.Pass();
308
309 decoder_->set_engine(gpu_scheduler_.get());
310
311 if (is_offscreen)
312 surface_ = gfx::GLSurface::CreateOffscreenGLSurface(size);
313 else
314 surface_ = gfx::GLSurface::CreateViewGLSurface(window);
315
316 if (!surface_.get()) {
317 LOG(ERROR) << "Could not create GLSurface.";
318 Destroy();
319 return false;
320 }
321
322 if (g_use_virtualized_gl_context) {
323 context_ = share_group->GetSharedContext();
324 if (!context_.get()) {
325 context_ = gfx::GLContext::CreateGLContext(
326 share_group.get(), surface_.get(), gpu_preference);
327 share_group->SetSharedContext(context_.get());
328 }
329
330 context_ = new GLContextVirtual(
331 share_group.get(), context_.get(), decoder_->AsWeakPtr());
332 if (context_->Initialize(surface_.get(), gpu_preference)) {
333 VLOG(1) << "Created virtual GL context.";
334 } else {
335 context_ = NULL;
336 }
337 } else {
338 context_ = gfx::GLContext::CreateGLContext(
339 share_group.get(), surface_.get(), gpu_preference);
340 }
341
342 if (!context_.get()) {
343 LOG(ERROR) << "Could not create GLContext.";
344 Destroy();
345 return false;
346 }
347
348 if (!context_->MakeCurrent(surface_.get())) {
349 LOG(ERROR) << "Could not make context current.";
350 Destroy();
351 return false;
352 }
353
354 gles2::DisallowedFeatures disallowed_features;
355 disallowed_features.swap_buffer_complete_callback = true;
356 disallowed_features.gpu_memory_manager = true;
357 if (!decoder_->Initialize(surface_,
358 context_,
359 is_offscreen,
360 size,
361 disallowed_features,
362 allowed_extensions,
363 attribs)) {
364 LOG(ERROR) << "Could not initialize decoder.";
365 Destroy();
366 return false;
367 }
368
369 if (!is_offscreen) {
370 decoder_->SetResizeCallback(base::Bind(
371 &InProcessCommandBuffer::OnResizeView, base::Unretained(this)));
372 }
373
374 if (share_resources_) {
375 g_all_shared_contexts.Pointer()->insert(this);
376 }
377
378 return true;
379 }
380
381 void InProcessCommandBuffer::Destroy() {
382 base::WaitableEvent completion(true, false);
383 bool result;
384 base::Callback<bool(void)> destroy_task = base::Bind(
385 &InProcessCommandBuffer::DestroyOnGpuThread, base::Unretained(this));
386 QueueTask(
387 base::Bind(&RunTaskWithResult<bool>, destroy_task, &result, &completion));
388 completion.Wait();
389 }
390
391 bool InProcessCommandBuffer::DestroyOnGpuThread() {
392 command_buffer_.reset();
393 context_ = NULL;
394 surface_ = NULL;
395 if (decoder_) {
396 decoder_->Destroy(!IsContextLost());
397 decoder_.reset();
398 }
399
400 g_all_shared_contexts.Pointer()->erase(this);
401 return true;
402 }
403
404 unsigned int InProcessCommandBuffer::CreateImageForGpuMemoryBuffer(
405 gfx::GpuMemoryBufferHandle buffer,
406 gfx::Size size) {
407 unsigned int image_id;
408 {
409 // TODO: ID allocation should go through CommandBuffer
410 base::AutoLock lock(command_buffer_lock_);
411 gles2::ContextGroup* group = decoder_->GetContextGroup();
412 image_id =
413 group->GetIdAllocator(gles2::id_namespaces::kImages)->AllocateID();
414 }
415 base::Closure image_task =
416 base::Bind(&InProcessCommandBuffer::CreateImageOnGpuThread,
417 base::Unretained(this), buffer, size, image_id);
418 QueueTask(image_task);
419 return image_id;
420 }
421
422 void InProcessCommandBuffer::CreateImageOnGpuThread(
423 gfx::GpuMemoryBufferHandle buffer,
424 gfx::Size size,
425 unsigned int image_id) {
426 scoped_refptr<gfx::GLImage> gl_image =
427 gfx::GLImage::CreateGLImageForGpuMemoryBuffer(buffer, size);
428 decoder_->GetContextGroup()->image_manager()->AddImage(gl_image, image_id);
429 }
430
431 void InProcessCommandBuffer::RemoveImage(unsigned int image_id) {
432 {
433 // TODO: ID allocation should go through CommandBuffer
434 base::AutoLock lock(command_buffer_lock_);
435 gles2::ContextGroup* group = decoder_->GetContextGroup();
436 group->GetIdAllocator(gles2::id_namespaces::kImages)->FreeID(image_id);
437 }
438 base::Closure image_manager_task =
439 base::Bind(&InProcessCommandBuffer::RemoveImageOnGpuThread,
440 base::Unretained(this),
441 image_id);
442 QueueTask(image_manager_task);
443 }
444
445 void InProcessCommandBuffer::RemoveImageOnGpuThread(unsigned int image_id) {
446 decoder_->GetContextGroup()->image_manager()->RemoveImage(image_id);
447 }
448
449 void InProcessCommandBuffer::OnContextLost() {
450 if (!context_lost_callback_.is_null())
451 context_lost_callback_.Run();
452
453 context_lost_ = true;
454 if (share_resources_) {
455 for (std::set<InProcessCommandBuffer*>::iterator it =
456 g_all_shared_contexts.Get().begin();
457 it != g_all_shared_contexts.Get().end();
458 ++it) {
459 (*it)->context_lost_ = true;
460 if (!(*it)->context_lost_callback_.is_null() && (*it) != this)
461 (*it)->context_lost_callback_.Run();
462 }
463 }
464 }
465
466 CommandBuffer::State InProcessCommandBuffer::GetStateFast() {
467 base::AutoLock lock(command_buffer_lock_);
468 return last_state_ = command_buffer_->GetState();
469 }
470
471 CommandBuffer::State InProcessCommandBuffer::GetState() {
472 return GetStateFast();
473 }
474
475 CommandBuffer::State InProcessCommandBuffer::GetLastState() {
476 return last_state_;
477 }
478
479 int32 InProcessCommandBuffer::GetLastToken() { return last_state_.token; }
480
481 void InProcessCommandBuffer::FlushOnGpuThread(int32 put_offset) {
482 base::AutoLock lock(command_buffer_lock_);
483 command_buffer_->Flush(put_offset);
484 }
485
486 void InProcessCommandBuffer::Flush(int32 put_offset) {
487 if (last_state_.error != gpu::error::kNoError)
488 return;
489
490 if (last_put_offset_ == put_offset)
491 return;
492
493 last_put_offset_ = put_offset;
494 base::Closure task = base::Bind(&InProcessCommandBuffer::FlushOnGpuThread,
495 base::Unretained(this),
496 put_offset);
497 QueueTask(task);
498 }
499
500 CommandBuffer::State InProcessCommandBuffer::FlushSync(int32 put_offset,
501 int32 last_known_get) {
502 if (put_offset == last_known_get || last_state_.error != gpu::error::kNoError)
503 return last_state_;
504
505 Flush(put_offset);
506 GetStateFast();
507 while (last_known_get == last_state_.get_offset &&
508 last_state_.error == gpu::error::kNoError) {
509 flush_event_.Wait();
510 GetStateFast();
511 }
512
513 return last_state_;
514 }
515
516 void InProcessCommandBuffer::SetGetBuffer(int32 shm_id) {
517 if (last_state_.error != gpu::error::kNoError)
518 return;
519
520 {
521 base::AutoLock lock(command_buffer_lock_);
522 command_buffer_->SetGetBuffer(shm_id);
523 last_put_offset_ = 0;
524 }
525 GetStateFast();
526 }
527
528 gpu::Buffer InProcessCommandBuffer::CreateTransferBuffer(size_t size,
529 int32* id) {
530 base::AutoLock lock(command_buffer_lock_);
531 return command_buffer_->CreateTransferBuffer(size, id);
532 }
533
534 void InProcessCommandBuffer::DestroyTransferBuffer(int32 id) {
535 base::AutoLock lock(command_buffer_lock_);
536 base::Closure task = base::Bind(&CommandBuffer::DestroyTransferBuffer,
537 base::Unretained(command_buffer_.get()),
538 id);
539
540 QueueTask(task);
541 }
542
543 gpu::Buffer InProcessCommandBuffer::GetTransferBuffer(int32 id) {
544 NOTREACHED();
545 return gpu::Buffer();
546 }
547
548 uint32 InProcessCommandBuffer::InsertSyncPoint() {
549 NOTREACHED();
550 return 0;
551 }
552 void InProcessCommandBuffer::SignalSyncPoint(unsigned sync_point,
553 const base::Closure& callback) {
554 QueueTask(WrapCallback(callback));
555 }
556
557 gpu::error::Error InProcessCommandBuffer::GetLastError() {
558 return last_state_.error;
559 }
560
561 bool InProcessCommandBuffer::Initialize() {
562 NOTREACHED();
563 return false;
564 }
565
566 void InProcessCommandBuffer::SetGetOffset(int32 get_offset) { NOTREACHED(); }
567
568 void InProcessCommandBuffer::SetToken(int32 token) { NOTREACHED(); }
569
570 void InProcessCommandBuffer::SetParseError(gpu::error::Error error) {
571 NOTREACHED();
572 }
573
574 void InProcessCommandBuffer::SetContextLostReason(
575 gpu::error::ContextLostReason reason) {
576 NOTREACHED();
577 }
578
579 static void PostCallback(const scoped_refptr<base::MessageLoopProxy>& loop,
580 const base::Closure& callback) {
581 if (loop != base::MessageLoopProxy::current())
582 loop->PostTask(FROM_HERE, callback);
583 else
584 callback.Run();
585 }
586
587 base::Closure InProcessCommandBuffer::WrapCallback(
588 const base::Closure& callback) {
589 base::Closure wrapped_callback =
590 base::Bind(&PostCallback, base::MessageLoopProxy::current(), callback);
591 return wrapped_callback;
592 }
593
594 // static
595 void InProcessCommandBuffer::EnableVirtualizedContext() {
596 g_use_virtualized_gl_context = true;
597 }
598
599 // static
600 void InProcessCommandBuffer::SetScheduleCallback(
601 const base::Closure& callback) {
602 g_gpu_queue.Get().SetScheduleCallback(callback);
603 }
604
605 // static
606 void InProcessCommandBuffer::ProcessGpuWorkOnCurrentThread() {
607 g_gpu_queue.Get().RunTasks();
608 }
609
610 } // namespace gpu
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698