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.
4
5#include "gpu/command_buffer/service/async_pixel_transfer_manager_idle.h"
6
7#include "base/bind.h"
8#include "base/debug/trace_event.h"
9#include "base/debug/trace_event_synthetic_delay.h"
10#include "base/lazy_instance.h"
11#include "base/memory/weak_ptr.h"
12#include "ui/gl/scoped_binders.h"
13
14namespace gpu {
15
16namespace {
17
18static uint64 g_next_pixel_transfer_state_id = 1;
19
20void PerformNotifyCompletion(
21    AsyncMemoryParams mem_params,
22    scoped_refptr<AsyncPixelTransferCompletionObserver> observer) {
23  TRACE_EVENT0("gpu", "PerformNotifyCompletion");
24  observer->DidComplete(mem_params);
25}
26
27}  // namespace
28
29// Class which handles async pixel transfers in a platform
30// independent way.
31class AsyncPixelTransferDelegateIdle
32    : public AsyncPixelTransferDelegate,
33      public base::SupportsWeakPtr<AsyncPixelTransferDelegateIdle> {
34 public:
35  AsyncPixelTransferDelegateIdle(
36      AsyncPixelTransferManagerIdle::SharedState* state,
37      GLuint texture_id,
38      const AsyncTexImage2DParams& define_params);
39  virtual ~AsyncPixelTransferDelegateIdle();
40
41  // Implement AsyncPixelTransferDelegate:
42  virtual void AsyncTexImage2D(
43      const AsyncTexImage2DParams& tex_params,
44      const AsyncMemoryParams& mem_params,
45      const base::Closure& bind_callback) OVERRIDE;
46  virtual void AsyncTexSubImage2D(
47      const AsyncTexSubImage2DParams& tex_params,
48      const AsyncMemoryParams& mem_params) OVERRIDE;
49  virtual bool TransferIsInProgress() OVERRIDE;
50  virtual void WaitForTransferCompletion() OVERRIDE;
51
52 private:
53  void PerformAsyncTexImage2D(AsyncTexImage2DParams tex_params,
54                              AsyncMemoryParams mem_params,
55                              const base::Closure& bind_callback);
56  void PerformAsyncTexSubImage2D(AsyncTexSubImage2DParams tex_params,
57                                 AsyncMemoryParams mem_params);
58
59  uint64 id_;
60  GLuint texture_id_;
61  bool transfer_in_progress_;
62  AsyncTexImage2DParams define_params_;
63
64  // Safe to hold a raw pointer because SharedState is owned by the Manager
65  // which owns the Delegate.
66  AsyncPixelTransferManagerIdle::SharedState* shared_state_;
67
68  DISALLOW_COPY_AND_ASSIGN(AsyncPixelTransferDelegateIdle);
69};
70
71AsyncPixelTransferDelegateIdle::AsyncPixelTransferDelegateIdle(
72    AsyncPixelTransferManagerIdle::SharedState* shared_state,
73    GLuint texture_id,
74    const AsyncTexImage2DParams& define_params)
75    : id_(g_next_pixel_transfer_state_id++),
76      texture_id_(texture_id),
77      transfer_in_progress_(false),
78      define_params_(define_params),
79      shared_state_(shared_state) {}
80
81AsyncPixelTransferDelegateIdle::~AsyncPixelTransferDelegateIdle() {}
82
83void AsyncPixelTransferDelegateIdle::AsyncTexImage2D(
84    const AsyncTexImage2DParams& tex_params,
85    const AsyncMemoryParams& mem_params,
86    const base::Closure& bind_callback) {
87  TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage");
88  DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target);
89
90  shared_state_->tasks.push_back(AsyncPixelTransferManagerIdle::Task(
91      id_,
92      this,
93      base::Bind(&AsyncPixelTransferDelegateIdle::PerformAsyncTexImage2D,
94                 AsWeakPtr(),
95                 tex_params,
96                 mem_params,
97                 bind_callback)));
98
99  transfer_in_progress_ = true;
100}
101
102void AsyncPixelTransferDelegateIdle::AsyncTexSubImage2D(
103    const AsyncTexSubImage2DParams& tex_params,
104    const AsyncMemoryParams& mem_params) {
105  TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("gpu.AsyncTexImage");
106  DCHECK_EQ(static_cast<GLenum>(GL_TEXTURE_2D), tex_params.target);
107
108  shared_state_->tasks.push_back(AsyncPixelTransferManagerIdle::Task(
109      id_,
110      this,
111      base::Bind(&AsyncPixelTransferDelegateIdle::PerformAsyncTexSubImage2D,
112                 AsWeakPtr(),
113                 tex_params,
114                 mem_params)));
115
116  transfer_in_progress_ = true;
117}
118
119bool  AsyncPixelTransferDelegateIdle::TransferIsInProgress() {
120  return transfer_in_progress_;
121}
122
123void AsyncPixelTransferDelegateIdle::WaitForTransferCompletion() {
124  for (std::list<AsyncPixelTransferManagerIdle::Task>::iterator iter =
125           shared_state_->tasks.begin();
126       iter != shared_state_->tasks.end();
127       ++iter) {
128    if (iter->transfer_id != id_)
129      continue;
130
131    (*iter).task.Run();
132    shared_state_->tasks.erase(iter);
133    break;
134  }
135
136  shared_state_->ProcessNotificationTasks();
137}
138
139void AsyncPixelTransferDelegateIdle::PerformAsyncTexImage2D(
140    AsyncTexImage2DParams tex_params,
141    AsyncMemoryParams mem_params,
142    const base::Closure& bind_callback) {
143  TRACE_EVENT2("gpu", "PerformAsyncTexImage2D",
144               "width", tex_params.width,
145               "height", tex_params.height);
146
147  void* data = mem_params.GetDataAddress();
148
149  base::TimeTicks begin_time(base::TimeTicks::HighResNow());
150  gfx::ScopedTextureBinder texture_binder(tex_params.target, texture_id_);
151
152  {
153    TRACE_EVENT0("gpu", "glTexImage2D");
154    glTexImage2D(
155        tex_params.target,
156        tex_params.level,
157        tex_params.internal_format,
158        tex_params.width,
159        tex_params.height,
160        tex_params.border,
161        tex_params.format,
162        tex_params.type,
163        data);
164  }
165
166  TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage");
167  transfer_in_progress_ = false;
168  shared_state_->texture_upload_count++;
169  shared_state_->total_texture_upload_time +=
170      base::TimeTicks::HighResNow() - begin_time;
171
172  // The texture is already fully bound so just call it now.
173  bind_callback.Run();
174}
175
176void AsyncPixelTransferDelegateIdle::PerformAsyncTexSubImage2D(
177    AsyncTexSubImage2DParams tex_params,
178    AsyncMemoryParams mem_params) {
179  TRACE_EVENT2("gpu", "PerformAsyncTexSubImage2D",
180               "width", tex_params.width,
181               "height", tex_params.height);
182
183  void* data = mem_params.GetDataAddress();
184
185  base::TimeTicks begin_time(base::TimeTicks::HighResNow());
186  gfx::ScopedTextureBinder texture_binder(tex_params.target, texture_id_);
187
188  // If it's a full texture update, use glTexImage2D as it's faster.
189  // TODO(epenner): Make this configurable (http://crbug.com/259924)
190  if (tex_params.xoffset == 0 &&
191      tex_params.yoffset == 0 &&
192      tex_params.target == define_params_.target &&
193      tex_params.level  == define_params_.level &&
194      tex_params.width  == define_params_.width &&
195      tex_params.height == define_params_.height) {
196    TRACE_EVENT0("gpu", "glTexImage2D");
197    glTexImage2D(
198        define_params_.target,
199        define_params_.level,
200        define_params_.internal_format,
201        define_params_.width,
202        define_params_.height,
203        define_params_.border,
204        tex_params.format,
205        tex_params.type,
206        data);
207  } else {
208    TRACE_EVENT0("gpu", "glTexSubImage2D");
209    glTexSubImage2D(
210        tex_params.target,
211        tex_params.level,
212        tex_params.xoffset,
213        tex_params.yoffset,
214        tex_params.width,
215        tex_params.height,
216        tex_params.format,
217        tex_params.type,
218        data);
219  }
220
221  TRACE_EVENT_SYNTHETIC_DELAY_END("gpu.AsyncTexImage");
222  transfer_in_progress_ = false;
223  shared_state_->texture_upload_count++;
224  shared_state_->total_texture_upload_time +=
225      base::TimeTicks::HighResNow() - begin_time;
226}
227
228AsyncPixelTransferManagerIdle::Task::Task(
229    uint64 transfer_id,
230    AsyncPixelTransferDelegate* delegate,
231    const base::Closure& task)
232    : transfer_id(transfer_id),
233      delegate(delegate),
234      task(task) {
235}
236
237AsyncPixelTransferManagerIdle::Task::~Task() {}
238
239AsyncPixelTransferManagerIdle::SharedState::SharedState()
240    : texture_upload_count(0) {}
241
242AsyncPixelTransferManagerIdle::SharedState::~SharedState() {}
243
244void AsyncPixelTransferManagerIdle::SharedState::ProcessNotificationTasks() {
245  while (!tasks.empty()) {
246    // Stop when we reach a pixel transfer task.
247    if (tasks.front().transfer_id)
248      return;
249
250    tasks.front().task.Run();
251    tasks.pop_front();
252  }
253}
254
255AsyncPixelTransferManagerIdle::AsyncPixelTransferManagerIdle()
256  : shared_state_() {
257}
258
259AsyncPixelTransferManagerIdle::~AsyncPixelTransferManagerIdle() {}
260
261void AsyncPixelTransferManagerIdle::BindCompletedAsyncTransfers() {
262  // Everything is already bound.
263}
264
265void AsyncPixelTransferManagerIdle::AsyncNotifyCompletion(
266    const AsyncMemoryParams& mem_params,
267    AsyncPixelTransferCompletionObserver* observer) {
268  if (shared_state_.tasks.empty()) {
269    observer->DidComplete(mem_params);
270    return;
271  }
272
273  shared_state_.tasks.push_back(
274      Task(0,  // 0 transfer_id for notification tasks.
275           NULL,
276           base::Bind(
277               &PerformNotifyCompletion,
278               mem_params,
279               make_scoped_refptr(observer))));
280}
281
282uint32 AsyncPixelTransferManagerIdle::GetTextureUploadCount() {
283  return shared_state_.texture_upload_count;
284}
285
286base::TimeDelta AsyncPixelTransferManagerIdle::GetTotalTextureUploadTime() {
287  return shared_state_.total_texture_upload_time;
288}
289
290void AsyncPixelTransferManagerIdle::ProcessMorePendingTransfers() {
291  if (shared_state_.tasks.empty())
292    return;
293
294  // First task should always be a pixel transfer task.
295  DCHECK(shared_state_.tasks.front().transfer_id);
296  shared_state_.tasks.front().task.Run();
297  shared_state_.tasks.pop_front();
298
299  shared_state_.ProcessNotificationTasks();
300}
301
302bool AsyncPixelTransferManagerIdle::NeedsProcessMorePendingTransfers() {
303  return !shared_state_.tasks.empty();
304}
305
306void AsyncPixelTransferManagerIdle::WaitAllAsyncTexImage2D() {
307  if (shared_state_.tasks.empty())
308    return;
309
310  const Task& task = shared_state_.tasks.back();
311  if (task.delegate)
312    task.delegate->WaitForTransferCompletion();
313}
314
315AsyncPixelTransferDelegate*
316AsyncPixelTransferManagerIdle::CreatePixelTransferDelegateImpl(
317    gles2::TextureRef* ref,
318    const AsyncTexImage2DParams& define_params) {
319  return new AsyncPixelTransferDelegateIdle(&shared_state_,
320                                            ref->service_id(),
321                                            define_params);
322}
323
324}  // namespace gpu
325