1 // Copyright 2013 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 "gpu/command_buffer/service/async_pixel_transfer_manager_share_group.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/synchronization/cancellation_flag.h"
15 #include "base/synchronization/lock.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "base/threading/thread.h"
18 #include "base/threading/thread_checker.h"
19 #include "base/trace_event/trace_event.h"
20 #include "base/trace_event/trace_event_synthetic_delay.h"
21 #include "gpu/command_buffer/service/async_pixel_transfer_delegate.h"
22 #include "ui/gl/gl_bindings.h"
23 #include "ui/gl/gl_context.h"
24 #include "ui/gl/gl_surface.h"
25 #include "ui/gl/gpu_preference.h"
26 #include "ui/gl/scoped_binders.h"
32 const char kAsyncTransferThreadName
[] = "AsyncTransferThread";
34 void PerformNotifyCompletion(
35 AsyncMemoryParams mem_params
,
36 scoped_refptr
<AsyncPixelTransferCompletionObserver
> observer
) {
37 TRACE_EVENT0("gpu", "PerformNotifyCompletion");
38 observer
->DidComplete(mem_params
);
41 // TODO(backer): Factor out common thread scheduling logic from the EGL and
42 // ShareGroup implementations. http://crbug.com/239889
43 class TransferThread
: public base::Thread
{
46 : base::Thread(kAsyncTransferThreadName
),
49 #if defined(OS_ANDROID) || defined(OS_LINUX)
50 SetPriority(base::ThreadPriority::BACKGROUND
);
54 ~TransferThread() override
{
55 // The only instance of this class was declared leaky.
59 void InitializeOnMainThread(gfx::GLContext
* parent_context
) {
60 TRACE_EVENT0("gpu", "TransferThread::InitializeOnMainThread");
64 base::WaitableEvent
wait_for_init(true, false);
65 message_loop_proxy()->PostTask(
67 base::Bind(&TransferThread::InitializeOnTransferThread
,
68 base::Unretained(this),
69 base::Unretained(parent_context
),
74 void CleanUp() override
{
82 scoped_refptr
<gfx::GLSurface
> surface_
;
83 scoped_refptr
<gfx::GLContext
> context_
;
85 void InitializeOnTransferThread(gfx::GLContext
* parent_context
,
86 base::WaitableEvent
* caller_wait
) {
87 TRACE_EVENT0("gpu", "InitializeOnTransferThread");
89 if (!parent_context
) {
90 LOG(ERROR
) << "No parent context provided.";
91 caller_wait
->Signal();
95 surface_
= gfx::GLSurface::CreateOffscreenGLSurface(gfx::Size(1, 1));
96 if (!surface_
.get()) {
97 LOG(ERROR
) << "Unable to create GLSurface";
98 caller_wait
->Signal();
102 // TODO(backer): This is coded for integrated GPUs. For discrete GPUs
103 // we would probably want to use a PBO texture upload for a true async
104 // upload (that would hopefully be optimized as a DMA transfer by the
106 context_
= gfx::GLContext::CreateGLContext(parent_context
->share_group(),
108 gfx::PreferIntegratedGpu
);
109 if (!context_
.get()) {
110 LOG(ERROR
) << "Unable to create GLContext.";
111 caller_wait
->Signal();
115 context_
->MakeCurrent(surface_
.get());
117 caller_wait
->Signal();
120 DISALLOW_COPY_AND_ASSIGN(TransferThread
);
123 base::LazyInstance
<TransferThread
>::Leaky
124 g_transfer_thread
= LAZY_INSTANCE_INITIALIZER
;
126 base::MessageLoopProxy
* transfer_message_loop_proxy() {
127 return g_transfer_thread
.Pointer()->message_loop_proxy().get();
130 class PendingTask
: public base::RefCountedThreadSafe
<PendingTask
> {
132 explicit PendingTask(const base::Closure
& task
)
133 : task_(task
), task_pending_(true, false) {}
136 // This is meant to be called on the main thread where the texture
138 DCHECK(checker_
.CalledOnValidThread());
139 if (task_lock_
.Try()) {
141 if (!task_
.is_null())
145 task_lock_
.Release();
146 task_pending_
.Signal();
152 void BindAndRun(GLuint texture_id
) {
153 // This is meant to be called on the upload thread where we don't have to
154 // restore the previous texture binding.
155 DCHECK(!checker_
.CalledOnValidThread());
156 base::AutoLock
locked(task_lock_
);
157 if (!task_
.is_null()) {
158 glBindTexture(GL_TEXTURE_2D
, texture_id
);
161 glBindTexture(GL_TEXTURE_2D
, 0);
162 // Flush for synchronization between threads.
164 task_pending_
.Signal();
169 base::AutoLock
locked(task_lock_
);
171 task_pending_
.Signal();
174 bool TaskIsInProgress() {
175 return !task_pending_
.IsSignaled();
179 task_pending_
.Wait();
183 friend class base::RefCountedThreadSafe
<PendingTask
>;
185 virtual ~PendingTask() {}
187 base::ThreadChecker checker_
;
189 base::Lock task_lock_
;
191 base::WaitableEvent task_pending_
;
193 DISALLOW_COPY_AND_ASSIGN(PendingTask
);
196 // Class which holds async pixel transfers state.
197 // The texture_id is accessed by either thread, but everything
198 // else accessed only on the main thread.
199 class TransferStateInternal
200 : public base::RefCountedThreadSafe
<TransferStateInternal
> {
202 TransferStateInternal(GLuint texture_id
,
203 const AsyncTexImage2DParams
& define_params
)
204 : texture_id_(texture_id
), define_params_(define_params
) {}
206 bool TransferIsInProgress() {
207 return pending_upload_task_
.get() &&
208 pending_upload_task_
->TaskIsInProgress();
211 void BindTransfer() {
212 TRACE_EVENT2("gpu", "BindAsyncTransfer",
213 "width", define_params_
.width
,
214 "height", define_params_
.height
);
217 glBindTexture(GL_TEXTURE_2D
, texture_id_
);
218 bind_callback_
.Run();
221 void WaitForTransferCompletion() {
222 TRACE_EVENT0("gpu", "WaitForTransferCompletion");
223 DCHECK(pending_upload_task_
.get());
224 if (!pending_upload_task_
->TryRun()) {
225 pending_upload_task_
->WaitForTask();
227 pending_upload_task_
= NULL
;
230 void CancelUpload() {
231 TRACE_EVENT0("gpu", "CancelUpload");
232 if (pending_upload_task_
.get())
233 pending_upload_task_
->Cancel();
234 pending_upload_task_
= NULL
;
237 void ScheduleAsyncTexImage2D(
238 const AsyncTexImage2DParams tex_params
,
239 const AsyncMemoryParams mem_params
,
240 scoped_refptr
<AsyncPixelTransferUploadStats
> texture_upload_stats
,
241 const base::Closure
& bind_callback
) {
242 TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage");
243 pending_upload_task_
= new PendingTask(base::Bind(
244 &TransferStateInternal::PerformAsyncTexImage2D
,
248 texture_upload_stats
));
249 transfer_message_loop_proxy()->PostTask(
252 &PendingTask::BindAndRun
, pending_upload_task_
, texture_id_
));
254 // Save the late bind callback, so we can notify the client when it is
256 bind_callback_
= bind_callback
;
259 void ScheduleAsyncTexSubImage2D(
260 AsyncTexSubImage2DParams tex_params
,
261 AsyncMemoryParams mem_params
,
262 scoped_refptr
<AsyncPixelTransferUploadStats
> texture_upload_stats
) {
263 TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage");
264 pending_upload_task_
= new PendingTask(base::Bind(
265 &TransferStateInternal::PerformAsyncTexSubImage2D
,
269 texture_upload_stats
));
270 transfer_message_loop_proxy()->PostTask(
273 &PendingTask::BindAndRun
, pending_upload_task_
, texture_id_
));
277 friend class base::RefCountedThreadSafe
<TransferStateInternal
>;
279 virtual ~TransferStateInternal() {
282 void PerformAsyncTexImage2D(
283 AsyncTexImage2DParams tex_params
,
284 AsyncMemoryParams mem_params
,
285 scoped_refptr
<AsyncPixelTransferUploadStats
> texture_upload_stats
) {
287 "PerformAsyncTexImage",
292 DCHECK_EQ(0, tex_params
.level
);
294 base::TimeTicks begin_time
;
295 if (texture_upload_stats
.get())
296 begin_time
= base::TimeTicks::Now();
298 void* data
= mem_params
.GetDataAddress();
301 TRACE_EVENT0("gpu", "glTexImage2D");
302 glTexImage2D(GL_TEXTURE_2D
,
304 tex_params
.internal_format
,
311 TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage");
314 if (texture_upload_stats
.get()) {
315 texture_upload_stats
->AddUpload(base::TimeTicks::Now() - begin_time
);
319 void PerformAsyncTexSubImage2D(
320 AsyncTexSubImage2DParams tex_params
,
321 AsyncMemoryParams mem_params
,
322 scoped_refptr
<AsyncPixelTransferUploadStats
> texture_upload_stats
) {
324 "PerformAsyncTexSubImage2D",
329 DCHECK_EQ(0, tex_params
.level
);
331 base::TimeTicks begin_time
;
332 if (texture_upload_stats
.get())
333 begin_time
= base::TimeTicks::Now();
335 void* data
= mem_params
.GetDataAddress();
337 TRACE_EVENT0("gpu", "glTexSubImage2D");
338 glTexSubImage2D(GL_TEXTURE_2D
,
347 TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage");
350 if (texture_upload_stats
.get()) {
351 texture_upload_stats
->AddUpload(base::TimeTicks::Now() - begin_time
);
355 scoped_refptr
<PendingTask
> pending_upload_task_
;
359 // Definition params for texture that needs binding.
360 AsyncTexImage2DParams define_params_
;
362 // Callback to invoke when AsyncTexImage2D is complete
363 // and the client can safely use the texture. This occurs
364 // during BindCompletedAsyncTransfers().
365 base::Closure bind_callback_
;
370 class AsyncPixelTransferDelegateShareGroup
371 : public AsyncPixelTransferDelegate
,
372 public base::SupportsWeakPtr
<AsyncPixelTransferDelegateShareGroup
> {
374 AsyncPixelTransferDelegateShareGroup(
375 AsyncPixelTransferManagerShareGroup::SharedState
* shared_state
,
377 const AsyncTexImage2DParams
& define_params
);
378 ~AsyncPixelTransferDelegateShareGroup() override
;
380 void BindTransfer() { state_
->BindTransfer(); }
382 // Implement AsyncPixelTransferDelegate:
383 void AsyncTexImage2D(const AsyncTexImage2DParams
& tex_params
,
384 const AsyncMemoryParams
& mem_params
,
385 const base::Closure
& bind_callback
) override
;
386 void AsyncTexSubImage2D(const AsyncTexSubImage2DParams
& tex_params
,
387 const AsyncMemoryParams
& mem_params
) override
;
388 bool TransferIsInProgress() override
;
389 void WaitForTransferCompletion() override
;
392 // A raw pointer is safe because the SharedState is owned by the Manager,
393 // which owns this Delegate.
394 AsyncPixelTransferManagerShareGroup::SharedState
* shared_state_
;
395 scoped_refptr
<TransferStateInternal
> state_
;
397 DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateShareGroup
);
400 AsyncPixelTransferDelegateShareGroup::AsyncPixelTransferDelegateShareGroup(
401 AsyncPixelTransferManagerShareGroup::SharedState
* shared_state
,
403 const AsyncTexImage2DParams
& define_params
)
404 : shared_state_(shared_state
),
405 state_(new TransferStateInternal(texture_id
, define_params
)) {}
407 AsyncPixelTransferDelegateShareGroup::~AsyncPixelTransferDelegateShareGroup() {
408 TRACE_EVENT0("gpu", " ~AsyncPixelTransferDelegateShareGroup");
409 state_
->CancelUpload();
412 bool AsyncPixelTransferDelegateShareGroup::TransferIsInProgress() {
413 return state_
->TransferIsInProgress();
416 void AsyncPixelTransferDelegateShareGroup::WaitForTransferCompletion() {
417 if (state_
->TransferIsInProgress()) {
418 state_
->WaitForTransferCompletion();
419 DCHECK(!state_
->TransferIsInProgress());
422 // Fast track the BindTransfer, if applicable.
423 for (AsyncPixelTransferManagerShareGroup::SharedState::TransferQueue::iterator
424 iter
= shared_state_
->pending_allocations
.begin();
425 iter
!= shared_state_
->pending_allocations
.end();
427 if (iter
->get() != this)
430 shared_state_
->pending_allocations
.erase(iter
);
436 void AsyncPixelTransferDelegateShareGroup::AsyncTexImage2D(
437 const AsyncTexImage2DParams
& tex_params
,
438 const AsyncMemoryParams
& mem_params
,
439 const base::Closure
& bind_callback
) {
440 DCHECK(!state_
->TransferIsInProgress());
441 DCHECK_EQ(static_cast<GLenum
>(GL_TEXTURE_2D
), tex_params
.target
);
442 DCHECK_EQ(tex_params
.level
, 0);
444 shared_state_
->pending_allocations
.push_back(AsWeakPtr());
445 state_
->ScheduleAsyncTexImage2D(tex_params
,
447 shared_state_
->texture_upload_stats
,
451 void AsyncPixelTransferDelegateShareGroup::AsyncTexSubImage2D(
452 const AsyncTexSubImage2DParams
& tex_params
,
453 const AsyncMemoryParams
& mem_params
) {
454 TRACE_EVENT2("gpu", "AsyncTexSubImage2D",
455 "width", tex_params
.width
,
456 "height", tex_params
.height
);
457 DCHECK(!state_
->TransferIsInProgress());
458 DCHECK_EQ(static_cast<GLenum
>(GL_TEXTURE_2D
), tex_params
.target
);
459 DCHECK_EQ(tex_params
.level
, 0);
461 state_
->ScheduleAsyncTexSubImage2D(
462 tex_params
, mem_params
, shared_state_
->texture_upload_stats
);
465 AsyncPixelTransferManagerShareGroup::SharedState::SharedState()
466 // TODO(reveman): Skip this if --enable-gpu-benchmarking is not present.
467 : texture_upload_stats(new AsyncPixelTransferUploadStats
) {}
469 AsyncPixelTransferManagerShareGroup::SharedState::~SharedState() {}
471 AsyncPixelTransferManagerShareGroup::AsyncPixelTransferManagerShareGroup(
472 gfx::GLContext
* context
) {
473 g_transfer_thread
.Pointer()->InitializeOnMainThread(context
);
476 AsyncPixelTransferManagerShareGroup::~AsyncPixelTransferManagerShareGroup() {}
478 void AsyncPixelTransferManagerShareGroup::BindCompletedAsyncTransfers() {
479 scoped_ptr
<gfx::ScopedTextureBinder
> texture_binder
;
481 while (!shared_state_
.pending_allocations
.empty()) {
482 if (!shared_state_
.pending_allocations
.front().get()) {
483 shared_state_
.pending_allocations
.pop_front();
486 AsyncPixelTransferDelegateShareGroup
* delegate
=
487 shared_state_
.pending_allocations
.front().get();
488 // Terminate early, as all transfers finish in order, currently.
489 if (delegate
->TransferIsInProgress())
493 texture_binder
.reset(new gfx::ScopedTextureBinder(GL_TEXTURE_2D
, 0));
495 // Used to set tex info from the gles2 cmd decoder once upload has
496 // finished (it'll bind the texture and call a callback).
497 delegate
->BindTransfer();
499 shared_state_
.pending_allocations
.pop_front();
503 void AsyncPixelTransferManagerShareGroup::AsyncNotifyCompletion(
504 const AsyncMemoryParams
& mem_params
,
505 AsyncPixelTransferCompletionObserver
* observer
) {
506 // Post a PerformNotifyCompletion task to the upload thread. This task
507 // will run after all async transfers are complete.
508 transfer_message_loop_proxy()->PostTask(
510 base::Bind(&PerformNotifyCompletion
,
512 make_scoped_refptr(observer
)));
515 uint32
AsyncPixelTransferManagerShareGroup::GetTextureUploadCount() {
516 return shared_state_
.texture_upload_stats
->GetStats(NULL
);
520 AsyncPixelTransferManagerShareGroup::GetTotalTextureUploadTime() {
521 base::TimeDelta total_texture_upload_time
;
522 shared_state_
.texture_upload_stats
->GetStats(&total_texture_upload_time
);
523 return total_texture_upload_time
;
526 void AsyncPixelTransferManagerShareGroup::ProcessMorePendingTransfers() {
529 bool AsyncPixelTransferManagerShareGroup::NeedsProcessMorePendingTransfers() {
533 void AsyncPixelTransferManagerShareGroup::WaitAllAsyncTexImage2D() {
534 if (shared_state_
.pending_allocations
.empty())
537 AsyncPixelTransferDelegateShareGroup
* delegate
=
538 shared_state_
.pending_allocations
.back().get();
540 delegate
->WaitForTransferCompletion();
543 AsyncPixelTransferDelegate
*
544 AsyncPixelTransferManagerShareGroup::CreatePixelTransferDelegateImpl(
545 gles2::TextureRef
* ref
,
546 const AsyncTexImage2DParams
& define_params
) {
547 return new AsyncPixelTransferDelegateShareGroup(
548 &shared_state_
, ref
->service_id(), define_params
);