Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / gpu / command_buffer / service / gpu_scheduler.cc
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/gpu_scheduler.h"
6
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/compiler_specific.h"
10 #include "base/debug/trace_event.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/time/time.h"
13 #include "ui/gl/gl_bindings.h"
14 #include "ui/gl/gl_fence.h"
15 #include "ui/gl/gl_switches.h"
16
17 #if defined(OS_WIN)
18 #include "base/win/windows_version.h"
19 #endif
20
21 using ::base::SharedMemory;
22
23 namespace gpu {
24
25 const int64 kUnscheduleFenceTimeOutDelay = 10000;
26
27 #if defined(OS_WIN)
28 const int64 kRescheduleTimeOutDelay = 1000;
29 #endif
30
31 GpuScheduler::GpuScheduler(CommandBufferServiceBase* command_buffer,
32                            AsyncAPIInterface* handler,
33                            gles2::GLES2Decoder* decoder)
34     : command_buffer_(command_buffer),
35       handler_(handler),
36       decoder_(decoder),
37       unscheduled_count_(0),
38       rescheduled_count_(0),
39       was_preempted_(false),
40       reschedule_task_factory_(this) {}
41
42 GpuScheduler::~GpuScheduler() {
43 }
44
45 void GpuScheduler::PutChanged() {
46   TRACE_EVENT1(
47      "gpu", "GpuScheduler:PutChanged",
48      "decoder", decoder_ ? decoder_->GetLogger()->GetLogPrefix() : "None");
49
50   CommandBuffer::State state = command_buffer_->GetLastState();
51
52   // If there is no parser, exit.
53   if (!parser_.get()) {
54     DCHECK_EQ(state.get_offset, command_buffer_->GetPutOffset());
55     return;
56   }
57
58   parser_->set_put(command_buffer_->GetPutOffset());
59   if (state.error != error::kNoError)
60     return;
61
62   // Check that the GPU has passed all fences.
63   if (!PollUnscheduleFences())
64     return;
65
66   // One of the unschedule fence tasks might have unscheduled us.
67   if (!IsScheduled())
68     return;
69
70   base::TimeTicks begin_time(base::TimeTicks::HighResNow());
71   error::Error error = error::kNoError;
72   if (decoder_)
73     decoder_->BeginDecoding();
74   while (!parser_->IsEmpty()) {
75     if (IsPreempted())
76       break;
77
78     DCHECK(IsScheduled());
79     DCHECK(unschedule_fences_.empty());
80
81     error = parser_->ProcessCommands(CommandParser::kParseCommandsSlice);
82
83     if (error == error::kDeferCommandUntilLater) {
84       DCHECK_GT(unscheduled_count_, 0);
85       break;
86     }
87
88     // TODO(piman): various classes duplicate various pieces of state, leading
89     // to needlessly complex update logic. It should be possible to simply
90     // share the state across all of them.
91     command_buffer_->SetGetOffset(static_cast<int32>(parser_->get()));
92
93     if (error::IsError(error)) {
94       command_buffer_->SetContextLostReason(decoder_->GetContextLostReason());
95       command_buffer_->SetParseError(error);
96       break;
97     }
98
99     if (!command_processed_callback_.is_null())
100       command_processed_callback_.Run();
101
102     if (unscheduled_count_ > 0)
103       break;
104   }
105
106   if (decoder_) {
107     if (!error::IsError(error) && decoder_->WasContextLost()) {
108       command_buffer_->SetContextLostReason(decoder_->GetContextLostReason());
109       command_buffer_->SetParseError(error::kLostContext);
110     }
111     decoder_->EndDecoding();
112     decoder_->AddProcessingCommandsTime(
113         base::TimeTicks::HighResNow() - begin_time);
114   }
115 }
116
117 void GpuScheduler::SetScheduled(bool scheduled) {
118   TRACE_EVENT2("gpu", "GpuScheduler:SetScheduled", "this", this,
119                "new unscheduled_count_",
120                unscheduled_count_ + (scheduled? -1 : 1));
121   if (scheduled) {
122     // If the scheduler was rescheduled after a timeout, ignore the subsequent
123     // calls to SetScheduled when they eventually arrive until they are all
124     // accounted for.
125     if (rescheduled_count_ > 0) {
126       --rescheduled_count_;
127       return;
128     } else {
129       --unscheduled_count_;
130     }
131
132     DCHECK_GE(unscheduled_count_, 0);
133
134     if (unscheduled_count_ == 0) {
135       TRACE_EVENT_ASYNC_END1("gpu", "ProcessingSwap", this,
136                              "GpuScheduler", this);
137       // When the scheduler transitions from the unscheduled to the scheduled
138       // state, cancel the task that would reschedule it after a timeout.
139       reschedule_task_factory_.InvalidateWeakPtrs();
140
141       if (!scheduling_changed_callback_.is_null())
142         scheduling_changed_callback_.Run(true);
143     }
144   } else {
145     ++unscheduled_count_;
146     if (unscheduled_count_ == 1) {
147       TRACE_EVENT_ASYNC_BEGIN1("gpu", "ProcessingSwap", this,
148                                "GpuScheduler", this);
149 #if defined(OS_WIN)
150       if (base::win::GetVersion() < base::win::VERSION_VISTA) {
151         // When the scheduler transitions from scheduled to unscheduled, post a
152         // delayed task that it will force it back into a scheduled state after
153         // a timeout. This should only be necessary on pre-Vista.
154         base::MessageLoop::current()->PostDelayedTask(
155             FROM_HERE,
156             base::Bind(&GpuScheduler::RescheduleTimeOut,
157                        reschedule_task_factory_.GetWeakPtr()),
158             base::TimeDelta::FromMilliseconds(kRescheduleTimeOutDelay));
159       }
160 #endif
161       if (!scheduling_changed_callback_.is_null())
162         scheduling_changed_callback_.Run(false);
163     }
164   }
165 }
166
167 bool GpuScheduler::IsScheduled() {
168   return unscheduled_count_ == 0;
169 }
170
171 bool GpuScheduler::HasMoreWork() {
172   return !unschedule_fences_.empty() ||
173          (decoder_ && decoder_->ProcessPendingQueries(false)) ||
174          HasMoreIdleWork();
175 }
176
177 void GpuScheduler::SetSchedulingChangedCallback(
178     const SchedulingChangedCallback& callback) {
179   scheduling_changed_callback_ = callback;
180 }
181
182 scoped_refptr<Buffer> GpuScheduler::GetSharedMemoryBuffer(int32 shm_id) {
183   return command_buffer_->GetTransferBuffer(shm_id);
184 }
185
186 void GpuScheduler::set_token(int32 token) {
187   command_buffer_->SetToken(token);
188 }
189
190 bool GpuScheduler::SetGetBuffer(int32 transfer_buffer_id) {
191   scoped_refptr<Buffer> ring_buffer =
192       command_buffer_->GetTransferBuffer(transfer_buffer_id);
193   if (!ring_buffer.get()) {
194     return false;
195   }
196
197   if (!parser_.get()) {
198     parser_.reset(new CommandParser(handler_));
199   }
200
201   parser_->SetBuffer(
202       ring_buffer->memory(), ring_buffer->size(), 0, ring_buffer->size());
203
204   SetGetOffset(0);
205   return true;
206 }
207
208 bool GpuScheduler::SetGetOffset(int32 offset) {
209   if (parser_->set_get(offset)) {
210     command_buffer_->SetGetOffset(static_cast<int32>(parser_->get()));
211     return true;
212   }
213   return false;
214 }
215
216 int32 GpuScheduler::GetGetOffset() {
217   return parser_->get();
218 }
219
220 void GpuScheduler::SetCommandProcessedCallback(
221     const base::Closure& callback) {
222   command_processed_callback_ = callback;
223 }
224
225 void GpuScheduler::DeferToFence(base::Closure task) {
226   unschedule_fences_.push(make_linked_ptr(
227        new UnscheduleFence(gfx::GLFence::Create(), task)));
228   SetScheduled(false);
229 }
230
231 bool GpuScheduler::PollUnscheduleFences() {
232   if (unschedule_fences_.empty())
233     return true;
234
235   if (unschedule_fences_.front()->fence.get()) {
236     base::Time now = base::Time::Now();
237     base::TimeDelta timeout =
238         base::TimeDelta::FromMilliseconds(kUnscheduleFenceTimeOutDelay);
239
240     while (!unschedule_fences_.empty()) {
241       const UnscheduleFence& fence = *unschedule_fences_.front();
242       if (fence.fence->HasCompleted() ||
243           now - fence.issue_time > timeout) {
244         unschedule_fences_.front()->task.Run();
245         unschedule_fences_.pop();
246         SetScheduled(true);
247       } else {
248         return false;
249       }
250     }
251   } else {
252     glFinish();
253
254     while (!unschedule_fences_.empty()) {
255       unschedule_fences_.front()->task.Run();
256       unschedule_fences_.pop();
257       SetScheduled(true);
258     }
259   }
260
261   return true;
262 }
263
264 bool GpuScheduler::IsPreempted() {
265   if (!preemption_flag_.get())
266     return false;
267
268   if (!was_preempted_ && preemption_flag_->IsSet()) {
269     TRACE_COUNTER_ID1("gpu", "GpuScheduler::Preempted", this, 1);
270     was_preempted_ = true;
271   } else if (was_preempted_ && !preemption_flag_->IsSet()) {
272     TRACE_COUNTER_ID1("gpu", "GpuScheduler::Preempted", this, 0);
273     was_preempted_ = false;
274   }
275
276   return preemption_flag_->IsSet();
277 }
278
279 bool GpuScheduler::HasMoreIdleWork() {
280   return (decoder_ && decoder_->HasMoreIdleWork());
281 }
282
283 void GpuScheduler::PerformIdleWork() {
284   if (!decoder_)
285     return;
286   decoder_->PerformIdleWork();
287 }
288
289 void GpuScheduler::RescheduleTimeOut() {
290   int new_count = unscheduled_count_ + rescheduled_count_;
291
292   rescheduled_count_ = 0;
293
294   while (unscheduled_count_)
295     SetScheduled(true);
296
297   rescheduled_count_ = new_count;
298 }
299
300 GpuScheduler::UnscheduleFence::UnscheduleFence(gfx::GLFence* fence_,
301                                                base::Closure task_)
302   : fence(fence_),
303     issue_time(base::Time::Now()),
304     task(task_) {
305 }
306
307 GpuScheduler::UnscheduleFence::~UnscheduleFence() {
308 }
309
310 }  // namespace gpu