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
14 namespace gpu {
15
16 namespace {
17
18 static uint64 g_next_pixel_transfer_state_id = 1;
19
PerformNotifyCompletion(AsyncMemoryParams mem_params,scoped_refptr<AsyncPixelTransferCompletionObserver> observer)20 void 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.
31 class 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
AsyncPixelTransferDelegateIdle(AsyncPixelTransferManagerIdle::SharedState * shared_state,GLuint texture_id,const AsyncTexImage2DParams & define_params)71 AsyncPixelTransferDelegateIdle::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
~AsyncPixelTransferDelegateIdle()81 AsyncPixelTransferDelegateIdle::~AsyncPixelTransferDelegateIdle() {}
82
AsyncTexImage2D(const AsyncTexImage2DParams & tex_params,const AsyncMemoryParams & mem_params,const base::Closure & bind_callback)83 void 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
AsyncTexSubImage2D(const AsyncTexSubImage2DParams & tex_params,const AsyncMemoryParams & mem_params)102 void 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
TransferIsInProgress()119 bool AsyncPixelTransferDelegateIdle::TransferIsInProgress() {
120 return transfer_in_progress_;
121 }
122
WaitForTransferCompletion()123 void 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
PerformAsyncTexImage2D(AsyncTexImage2DParams tex_params,AsyncMemoryParams mem_params,const base::Closure & bind_callback)139 void 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
PerformAsyncTexSubImage2D(AsyncTexSubImage2DParams tex_params,AsyncMemoryParams mem_params)176 void 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
Task(uint64 transfer_id,AsyncPixelTransferDelegate * delegate,const base::Closure & task)228 AsyncPixelTransferManagerIdle::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
~Task()237 AsyncPixelTransferManagerIdle::Task::~Task() {}
238
SharedState()239 AsyncPixelTransferManagerIdle::SharedState::SharedState()
240 : texture_upload_count(0) {}
241
~SharedState()242 AsyncPixelTransferManagerIdle::SharedState::~SharedState() {}
243
ProcessNotificationTasks()244 void 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
AsyncPixelTransferManagerIdle()255 AsyncPixelTransferManagerIdle::AsyncPixelTransferManagerIdle()
256 : shared_state_() {
257 }
258
~AsyncPixelTransferManagerIdle()259 AsyncPixelTransferManagerIdle::~AsyncPixelTransferManagerIdle() {}
260
BindCompletedAsyncTransfers()261 void AsyncPixelTransferManagerIdle::BindCompletedAsyncTransfers() {
262 // Everything is already bound.
263 }
264
AsyncNotifyCompletion(const AsyncMemoryParams & mem_params,AsyncPixelTransferCompletionObserver * observer)265 void 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
GetTextureUploadCount()282 uint32 AsyncPixelTransferManagerIdle::GetTextureUploadCount() {
283 return shared_state_.texture_upload_count;
284 }
285
GetTotalTextureUploadTime()286 base::TimeDelta AsyncPixelTransferManagerIdle::GetTotalTextureUploadTime() {
287 return shared_state_.total_texture_upload_time;
288 }
289
ProcessMorePendingTransfers()290 void 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
NeedsProcessMorePendingTransfers()302 bool AsyncPixelTransferManagerIdle::NeedsProcessMorePendingTransfers() {
303 return !shared_state_.tasks.empty();
304 }
305
WaitAllAsyncTexImage2D()306 void 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
315 AsyncPixelTransferDelegate*
CreatePixelTransferDelegateImpl(gles2::TextureRef * ref,const AsyncTexImage2DParams & define_params)316 AsyncPixelTransferManagerIdle::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