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

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 #include <vector>
10
11 #include <GLES2/gl2.h>
12 #ifndef GL_GLEXT_PROTOTYPES
13 #define GL_GLEXT_PROTOTYPES 1
14 #endif
15 #include <GLES2/gl2ext.h>
16 #include <GLES2/gl2extchromium.h>
17
18 #include "base/bind.h"
19 #include "base/bind_helpers.h"
20 #include "base/lazy_instance.h"
21 #include "base/logging.h"
22 #include "base/memory/scoped_ptr.h"
23 #include "base/memory/weak_ptr.h"
24 #include "base/message_loop/message_loop.h"
25 #include "base/message_loop/message_loop_proxy.h"
26 #include "base/synchronization/waitable_event.h"
27 #include "base/threading/non_thread_safe.h"
28 #include "base/threading/thread.h"
29 #include "gpu/command_buffer/common/id_allocator.h"
30 #include "gpu/command_buffer/service/command_buffer_service.h"
31 #include "gpu/command_buffer/service/context_group.h"
32 #include "gpu/command_buffer/service/gl_context_virtual.h"
33 #include "gpu/command_buffer/service/gpu_scheduler.h"
34 #include "gpu/command_buffer/service/image_manager.h"
35 #include "gpu/command_buffer/service/transfer_buffer_manager.h"
36 #include "ui/gfx/size.h"
37 #include "ui/gl/gl_context.h"
38 #include "ui/gl/gl_image.h"
39 #include "ui/gl/gl_share_group.h"
40 #include "ui/gl/gl_surface.h"
41
42 namespace gpu {
43
44 namespace {
45
46 static base::LazyInstance<std::set<InProcessCommandBuffer*> >
47 g_all_shared_contexts = LAZY_INSTANCE_INITIALIZER;
48
49 static bool g_use_virtualized_gl_context = false;
50
51 template <typename T>
52 static void RunTaskWithResult(base::Callback<T(void)> task,
53 T* result,
54 base::WaitableEvent* completion) {
55 *result = task.Run();
56 completion->Signal();
57 }
58
59 class GpuCommandQueue {
60 public:
61 GpuCommandQueue();
62 virtual ~GpuCommandQueue();
63
64 void QueueTask(const base::Closure& task);
65 void RunTasks();
66
67 private:
68 base::Lock tasks_lock_;
69 std::queue<base::Closure> tasks_;
70
71 DISALLOW_COPY_AND_ASSIGN(GpuCommandQueue);
72 };
73
74 GpuCommandQueue::GpuCommandQueue() {
75 }
76
77 GpuCommandQueue::~GpuCommandQueue() {
78 base::AutoLock lock(tasks_lock_);
79 DCHECK(tasks_.empty());
80 }
81
82 void GpuCommandQueue::QueueTask(const base::Closure& task) {
83 base::AutoLock lock(tasks_lock_);
84 tasks_.push(task);
85 }
86
87 void GpuCommandQueue::RunTasks() {
88 size_t num_tasks;
89 {
90 base::AutoLock lock(tasks_lock_);
91 num_tasks = tasks_.size();
92 }
93
94 while (num_tasks) {
95 base::Closure task;
96 {
97 base::AutoLock lock(tasks_lock_);
98 task = tasks_.front();
99 tasks_.pop();
100 num_tasks = tasks_.size();
101 }
102
103 task.Run();
104 }
105 }
106
107 static base::LazyInstance<base::Closure> g_schedule_work_callback =
108 LAZY_INSTANCE_INITIALIZER;
109
110 class GpuInProcessThread : public base::Thread {
111 public:
112 GpuInProcessThread();
113 virtual ~GpuInProcessThread();
114
115 private:
116 DISALLOW_COPY_AND_ASSIGN(GpuInProcessThread);
117 };
118
119 GpuInProcessThread::GpuInProcessThread() : base::Thread("GpuThread") {
120 Start();
121 }
122
123 GpuInProcessThread::~GpuInProcessThread() {}
124
125 static base::LazyInstance<GpuInProcessThread> g_gpu_thread =
126 LAZY_INSTANCE_INITIALIZER;
127
128 static base::LazyInstance<GpuCommandQueue> g_gpu_queue =
129 LAZY_INSTANCE_INITIALIZER;
piman 2013/07/23 01:57:14 I'm fairly uncomfortable with all these globals be
no sievers 2013/07/25 00:41:23 Done.
130
131 static void QueueTask(const base::Closure& task) {
132 g_gpu_queue.Get().QueueTask(task);
133
134 if (!g_schedule_work_callback.Get().is_null()) {
135 g_schedule_work_callback.Get().Run();
136 return;
137 }
138 g_gpu_thread.Get().message_loop()
139 ->PostTask(FROM_HERE,
140 base::Bind(&GpuCommandQueue::RunTasks,
141 base::Unretained(g_gpu_queue.Pointer())));
142 }
143
144 } // anonyous namespace
145
146 InProcessCommandBuffer::InProcessCommandBuffer()
147 : context_lost_(false), last_put_offset_(-1) {}
148
149 InProcessCommandBuffer::~InProcessCommandBuffer() {
150 Destroy();
151 }
152
153 bool InProcessCommandBuffer::IsContextLost() {
154 if (context_lost_ || !command_buffer_) {
155 return true;
156 }
157 CommandBuffer::State state = GetState();
158 return error::IsError(state.error);
159 }
160
161 void InProcessCommandBuffer::OnResizeView(gfx::Size size, float scale_factor) {
162 DCHECK(!surface_->IsOffscreen());
163 surface_->Resize(size);
164 }
165
166 bool InProcessCommandBuffer::MakeCurrent() {
167 if (decoder_->MakeCurrent())
168 return true;
169 DLOG(ERROR) << "Context lost because MakeCurrent failed.";
170 command_buffer_->SetContextLostReason(decoder_->GetContextLostReason());
171 command_buffer_->SetParseError(gpu::error::kLostContext);
172 return false;
173 }
174
175 void InProcessCommandBuffer::PumpCommands() {
176 base::AutoLock lock(service_lock_);
piman 2013/07/23 01:57:14 I think it would be safer if the lock was taken be
no sievers 2013/07/25 00:41:23 Oops, my bad. Done.
177
178 if (!MakeCurrent())
179 return;
180
181 gpu_scheduler_->PutChanged();
182 CommandBuffer::State state = command_buffer_->GetState();
183 DCHECK((!error::IsError(state.error) && !context_lost_) ||
184 (error::IsError(state.error) && context_lost_));
185 }
186
187 bool InProcessCommandBuffer::GetBufferChanged(int32 transfer_buffer_id) {
188 command_buffer_->SetGetBuffer(transfer_buffer_id);
189 return true;
190 }
191
192 bool InProcessCommandBuffer::Initialize(
193 bool is_offscreen,
194 bool share_resources,
195 gfx::AcceleratedWidget window,
196 const gfx::Size& size,
197 const char* allowed_extensions,
198 const std::vector<int32>& attribs,
199 gfx::GpuPreference gpu_preference,
200 const base::Closure& context_lost_callback) {
201
202 share_resources_ = share_resources;
203 context_lost_callback_ = WrapCallback(context_lost_callback);
204
205 base::WaitableEvent completion(true, false);
206 bool result;
207 base::Callback<bool(void)> init_task =
208 base::Bind(&InProcessCommandBuffer::InitializeOnGpuThread,
209 base::Unretained(this),
piman 2013/07/23 01:57:14 What ensures |this| outlives the thread? If the de
no sievers 2013/07/25 00:41:23 Was this comment intended for the Destroy() furthe
210 is_offscreen,
211 window,
212 size,
213 allowed_extensions,
214 attribs,
215 gpu_preference);
216 QueueTask(
217 base::Bind(&RunTaskWithResult<bool>, init_task, &result, &completion));
218 completion.Wait();
219 return result;
220 }
221
222 bool InProcessCommandBuffer::InitializeOnGpuThread(
223 bool is_offscreen,
224 gfx::AcceleratedWidget window,
225 const gfx::Size& size,
226 const char* allowed_extensions,
227 const std::vector<int32>& attribs,
228 gfx::GpuPreference gpu_preference) {
229 // Use one share group for all contexts.
230 CR_DEFINE_STATIC_LOCAL(scoped_refptr<gfx::GLShareGroup>, share_group,
231 (new gfx::GLShareGroup));
232
233 DCHECK(size.width() >= 0 && size.height() >= 0);
234
235 TransferBufferManager* manager = new TransferBufferManager();
236 transfer_buffer_manager_.reset(manager);
237 manager->Initialize();
238
239 scoped_ptr<CommandBufferService> command_buffer(
240 new CommandBufferService(transfer_buffer_manager_.get()));
241 command_buffer->SetPutOffsetChangeCallback(base::Bind(
242 &InProcessCommandBuffer::PumpCommands, base::Unretained(this)));
243 command_buffer->SetParseErrorCallback(base::Bind(
244 &InProcessCommandBuffer::OnContextLost, base::Unretained(this)));
245
246 if (!command_buffer->Initialize()) {
247 LOG(ERROR) << "Could not initialize command buffer.";
248 Destroy();
249 return false;
250 }
251
252 InProcessCommandBuffer* context_group = NULL;
253
254 if (share_resources_ && !g_all_shared_contexts.Get().empty()) {
255 for (std::set<InProcessCommandBuffer*>::iterator it =
256 g_all_shared_contexts.Get().begin();
257 it != g_all_shared_contexts.Get().end();
258 ++it) {
259 if (!(*it)->IsContextLost()) {
260 context_group = *it;
261 break;
262 }
263 }
264 if (!context_group)
265 share_group = new gfx::GLShareGroup;
266 }
267
268 // TODO(gman): This needs to be true if this is Pepper.
269 bool bind_generates_resource = false;
270 decoder_.reset(gles2::GLES2Decoder::Create(
271 context_group ? context_group->decoder_->GetContextGroup()
272 : new gles2::ContextGroup(
273 NULL, NULL, NULL, NULL, bind_generates_resource)));
274
275 gpu_scheduler_.reset(
276 new GpuScheduler(command_buffer.get(), decoder_.get(), decoder_.get()));
277 command_buffer->SetGetBufferChangeCallback(base::Bind(
278 &GpuScheduler::SetGetBuffer, base::Unretained(gpu_scheduler_.get())));
279 command_buffer_ = command_buffer.Pass();
280
281 decoder_->set_engine(gpu_scheduler_.get());
282
283 if (is_offscreen)
284 surface_ = gfx::GLSurface::CreateOffscreenGLSurface(size);
285 else
286 surface_ = gfx::GLSurface::CreateViewGLSurface(window);
287
288 if (!surface_.get()) {
289 LOG(ERROR) << "Could not create GLSurface.";
290 Destroy();
291 return false;
292 }
293
294 if (g_use_virtualized_gl_context) {
295 context_ = share_group->GetSharedContext();
296 if (!context_.get()) {
297 context_ = gfx::GLContext::CreateGLContext(
298 share_group.get(), surface_.get(), gpu_preference);
299 share_group->SetSharedContext(context_.get());
300 }
301
302 context_ = new GLContextVirtual(
303 share_group.get(), context_.get(), decoder_->AsWeakPtr());
304 if (context_->Initialize(surface_.get(), gpu_preference)) {
305 VLOG(1) << "Created virtual GL context.";
306 } else {
307 context_ = NULL;
308 }
309 } else {
310 context_ = gfx::GLContext::CreateGLContext(
311 share_group.get(), surface_.get(), gpu_preference);
312 }
313
314 if (!context_.get()) {
315 LOG(ERROR) << "Could not create GLContext.";
316 Destroy();
317 return false;
318 }
319
320 if (!context_->MakeCurrent(surface_.get())) {
321 LOG(ERROR) << "Could not make context current.";
322 Destroy();
323 return false;
324 }
325
326 gles2::DisallowedFeatures disallowed_features;
327 disallowed_features.swap_buffer_complete_callback = true;
328 disallowed_features.gpu_memory_manager = true;
329 if (!decoder_->Initialize(surface_,
330 context_,
331 is_offscreen,
332 size,
333 disallowed_features,
334 allowed_extensions,
335 attribs)) {
336 LOG(ERROR) << "Could not initialize decoder.";
337 Destroy();
338 return false;
339 }
340
341 if (!is_offscreen) {
342 decoder_->SetResizeCallback(base::Bind(
343 &InProcessCommandBuffer::OnResizeView, base::Unretained(this)));
344 }
345
346 if (share_resources_) {
347 g_all_shared_contexts.Pointer()->insert(this);
348 }
349
350 return true;
351 }
352
353 void InProcessCommandBuffer::Destroy() {
354 base::WaitableEvent completion(true, false);
355 bool result;
356 base::Callback<bool(void)> destroy_task = base::Bind(
357 &InProcessCommandBuffer::DestroyOnGpuThread, base::Unretained(this));
358 QueueTask(
359 base::Bind(&RunTaskWithResult<bool>, destroy_task, &result, &completion));
360 completion.Wait();
361 }
362
363 bool InProcessCommandBuffer::DestroyOnGpuThread() {
364 if (decoder_) {
365 decoder_->Destroy(!IsContextLost());
366 decoder_.reset();
367 }
368
369 g_all_shared_contexts.Pointer()->erase(this);
370 return true;
371 }
372
373 unsigned int InProcessCommandBuffer::AddImage(
374 scoped_refptr<gfx::GLImage> image) {
375 base::AutoLock lock(service_lock_);
376 gles2::ContextGroup* group = decoder_->GetContextGroup();
no sievers 2013/07/25 00:41:23 Oops, neither GLImage nor ImageManager are RefCoun
377 unsigned int image_id =
378 group->GetIdAllocator(gles2::id_namespaces::kImages)->AllocateID();
379 base::Closure image_manager_task = base::Bind(
380 &gles2::ImageManager::AddImage, group->image_manager(), image, image_id);
381 QueueTask(image_manager_task);
382 return image_id;
383 }
384
385 void InProcessCommandBuffer::RemoveImage(unsigned int image_id) {
386 base::AutoLock lock(service_lock_);
387 gles2::ContextGroup* group = decoder_->GetContextGroup();
388 group->GetIdAllocator(gles2::id_namespaces::kImages)->FreeID(image_id);
389 base::Closure image_manager_task = base::Bind(
390 &gles2::ImageManager::RemoveImage, group->image_manager(), image_id);
391 QueueTask(image_manager_task);
392 }
393
394 void InProcessCommandBuffer::OnContextLost() {
395 if (!context_lost_callback_.is_null())
396 context_lost_callback_.Run();
397
398 context_lost_ = true;
399 if (share_resources_) {
400 for (std::set<InProcessCommandBuffer*>::iterator it =
401 g_all_shared_contexts.Get().begin();
402 it != g_all_shared_contexts.Get().end();
403 ++it)
404 (*it)->context_lost_ = true;
405 }
406 }
407
408 CommandBuffer::State InProcessCommandBuffer::GetStateFast() {
409 base::AutoLock lock(service_lock_);
410 return last_state_ = command_buffer_->GetState();
411 }
412
413 CommandBuffer::State InProcessCommandBuffer::GetState() {
414 while (last_put_offset_ != last_state_.get_offset &&
415 last_state_.error == gpu::error::kNoError) {
416 GetStateFast();
417 base::PlatformThread::YieldCurrentThread();
418 }
piman 2013/07/23 01:57:14 I'm not sure what you mean to do here. It looks li
no sievers 2013/07/25 00:41:23 Done.
419
420 return last_state_;
421 }
422
423 CommandBuffer::State InProcessCommandBuffer::GetLastState() {
424 return last_state_;
425 }
426
427 int32 InProcessCommandBuffer::GetLastToken() { return last_state_.token; }
428
429 void InProcessCommandBuffer::Flush(int32 put_offset) {
430 if (last_state_.error != gpu::error::kNoError)
431 return;
432
433 if (last_put_offset_ == put_offset)
434 return;
435
436 last_put_offset_ = put_offset;
437 base::Closure task = base::Bind(&CommandBuffer::Flush,
438 base::Unretained(command_buffer_.get()),
439 put_offset);
440
441 QueueTask(task);
442 }
443
444 CommandBuffer::State InProcessCommandBuffer::FlushSync(int32 put_offset,
445 int32 last_known_get) {
446 Flush(put_offset);
447 if (last_known_get == last_state_.get_offset) {
448 GetStateFast();
449 base::PlatformThread::YieldCurrentThread();
450 }
piman 2013/07/23 01:57:14 The semantics of FlushSync is that it only returns
no sievers 2013/07/25 00:41:23 Hmm, I don't see CommandBufferProxyImpl guarantee
451
452 return last_state_;
453 }
454
455 void InProcessCommandBuffer::SetGetBuffer(int32 shm_id) {
456 if (last_state_.error != gpu::error::kNoError)
457 return;
458
459 {
460 base::AutoLock lock(service_lock_);
461 command_buffer_->SetGetBuffer(shm_id);
462 last_put_offset_ = 0;
463 }
464 GetStateFast();
465 }
466
467 gpu::Buffer InProcessCommandBuffer::CreateTransferBuffer(size_t size,
468 int32* id) {
469 base::AutoLock lock(service_lock_);
470 return command_buffer_->CreateTransferBuffer(size, id);
471 }
472
473 void InProcessCommandBuffer::DestroyTransferBuffer(int32 id) {
474 base::AutoLock lock(service_lock_);
475 command_buffer_->DestroyTransferBuffer(id);
piman 2013/07/23 01:57:14 mmh, this would execute out-of-order wrt the comma
no sievers 2013/07/25 00:41:23 It looked like the client only frees pending an ex
476 }
477
478 gpu::Buffer InProcessCommandBuffer::GetTransferBuffer(int32 id) {
479 NOTREACHED();
480 return gpu::Buffer();
481 }
482
483 uint32 InProcessCommandBuffer::InsertSyncPoint() {
484 NOTREACHED();
485 return 0;
486 }
487 void InProcessCommandBuffer::SignalSyncPoint(unsigned sync_point,
488 const base::Closure& callback) {
489 QueueTask(WrapCallback(callback));
490 }
491
492 gpu::error::Error InProcessCommandBuffer::GetLastError() {
493 return last_state_.error;
494 }
495
496 bool InProcessCommandBuffer::Initialize() {
497 NOTREACHED();
498 return false;
499 }
500
501 void InProcessCommandBuffer::SetGetOffset(int32 get_offset) { NOTREACHED(); }
502
503 void InProcessCommandBuffer::SetToken(int32 token) { NOTREACHED(); }
504
505 void InProcessCommandBuffer::SetParseError(gpu::error::Error error) {
506 NOTREACHED();
507 }
508
509 void InProcessCommandBuffer::SetContextLostReason(
510 gpu::error::ContextLostReason reason) {
511 NOTREACHED();
512 }
513
514 static void PostCallback(const scoped_refptr<base::MessageLoopProxy>& loop,
515 const base::Closure& callback) {
516 if (loop != base::MessageLoopProxy::current())
517 loop->PostTask(FROM_HERE, callback);
518 else
519 callback.Run();
520 }
521
522 base::Closure InProcessCommandBuffer::WrapCallback(
523 const base::Closure& callback) {
524 base::Closure wrapped_callback =
525 base::Bind(&PostCallback, base::MessageLoopProxy::current(), callback);
526 return wrapped_callback;
527 }
528
529 // static
530 void InProcessCommandBuffer::EnableVirtualizedContext() {
531 g_use_virtualized_gl_context = true;
532 }
533
534 // static
535 void InProcessCommandBuffer::SetScheduleCallback(
536 const base::Closure& callback) {
537 DCHECK(g_schedule_work_callback.Get().is_null());
538 g_schedule_work_callback.Get() = callback;
539 }
540
541 // static
542 void InProcessCommandBuffer::ProcessGpuWorkOnCurrentThread() {
543 g_gpu_queue.Get().RunTasks();
544 }
545
546 } // namespace gpu
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698