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.
5 #include "gpu/command_buffer/service/gpu_scheduler.h"
8 #include "base/command_line.h"
9 #include "base/compiler_specific.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/time/time.h"
12 #include "base/trace_event/trace_event.h"
13 #include "gpu/command_buffer/service/logger.h"
14 #include "ui/gl/gl_bindings.h"
15 #include "ui/gl/gl_fence.h"
16 #include "ui/gl/gl_switches.h"
19 #include "base/win/windows_version.h"
22 using ::base::SharedMemory
;
27 const int64 kRescheduleTimeOutDelay
= 1000;
30 GpuScheduler::GpuScheduler(CommandBufferServiceBase
* command_buffer
,
31 AsyncAPIInterface
* handler
,
32 gles2::GLES2Decoder
* decoder
)
33 : command_buffer_(command_buffer
),
36 unscheduled_count_(0),
37 rescheduled_count_(0),
38 was_preempted_(false),
39 reschedule_task_factory_(this) {}
41 GpuScheduler::~GpuScheduler() {
44 void GpuScheduler::PutChanged() {
46 "gpu", "GpuScheduler:PutChanged",
47 "decoder", decoder_
? decoder_
->GetLogger()->GetLogPrefix() : "None");
49 CommandBuffer::State state
= command_buffer_
->GetLastState();
51 // If there is no parser, exit.
53 DCHECK_EQ(state
.get_offset
, command_buffer_
->GetPutOffset());
57 parser_
->set_put(command_buffer_
->GetPutOffset());
58 if (state
.error
!= error::kNoError
)
61 // One of the unschedule fence tasks might have unscheduled us.
65 base::TimeTicks
begin_time(base::TimeTicks::Now());
66 error::Error error
= error::kNoError
;
68 decoder_
->BeginDecoding();
69 while (!parser_
->IsEmpty()) {
73 DCHECK(IsScheduled());
75 error
= parser_
->ProcessCommands(CommandParser::kParseCommandsSlice
);
77 if (error
== error::kDeferCommandUntilLater
) {
78 DCHECK_GT(unscheduled_count_
, 0);
82 // TODO(piman): various classes duplicate various pieces of state, leading
83 // to needlessly complex update logic. It should be possible to simply
84 // share the state across all of them.
85 command_buffer_
->SetGetOffset(static_cast<int32
>(parser_
->get()));
87 if (error::IsError(error
)) {
88 command_buffer_
->SetContextLostReason(decoder_
->GetContextLostReason());
89 command_buffer_
->SetParseError(error
);
93 if (!command_processed_callback_
.is_null())
94 command_processed_callback_
.Run();
96 if (unscheduled_count_
> 0)
101 if (!error::IsError(error
) && decoder_
->WasContextLost()) {
102 command_buffer_
->SetContextLostReason(decoder_
->GetContextLostReason());
103 command_buffer_
->SetParseError(error::kLostContext
);
105 decoder_
->EndDecoding();
106 decoder_
->AddProcessingCommandsTime(base::TimeTicks::Now() - begin_time
);
110 void GpuScheduler::SetScheduled(bool scheduled
) {
111 TRACE_EVENT2("gpu", "GpuScheduler:SetScheduled", "this", this,
112 "new unscheduled_count_",
113 unscheduled_count_
+ (scheduled
? -1 : 1));
115 // If the scheduler was rescheduled after a timeout, ignore the subsequent
116 // calls to SetScheduled when they eventually arrive until they are all
118 if (rescheduled_count_
> 0) {
119 --rescheduled_count_
;
122 --unscheduled_count_
;
125 DCHECK_GE(unscheduled_count_
, 0);
127 if (unscheduled_count_
== 0) {
128 TRACE_EVENT_ASYNC_END1("gpu", "ProcessingSwap", this,
129 "GpuScheduler", this);
130 // When the scheduler transitions from the unscheduled to the scheduled
131 // state, cancel the task that would reschedule it after a timeout.
132 reschedule_task_factory_
.InvalidateWeakPtrs();
134 if (!scheduling_changed_callback_
.is_null())
135 scheduling_changed_callback_
.Run(true);
138 ++unscheduled_count_
;
139 if (unscheduled_count_
== 1) {
140 TRACE_EVENT_ASYNC_BEGIN1("gpu", "ProcessingSwap", this,
141 "GpuScheduler", this);
143 if (base::win::GetVersion() < base::win::VERSION_VISTA
) {
144 // When the scheduler transitions from scheduled to unscheduled, post a
145 // delayed task that it will force it back into a scheduled state after
146 // a timeout. This should only be necessary on pre-Vista.
147 base::MessageLoop::current()->PostDelayedTask(
149 base::Bind(&GpuScheduler::RescheduleTimeOut
,
150 reschedule_task_factory_
.GetWeakPtr()),
151 base::TimeDelta::FromMilliseconds(kRescheduleTimeOutDelay
));
154 if (!scheduling_changed_callback_
.is_null())
155 scheduling_changed_callback_
.Run(false);
160 bool GpuScheduler::IsScheduled() {
161 return unscheduled_count_
== 0;
164 bool GpuScheduler::HasMoreWork() {
165 return (decoder_
&& decoder_
->ProcessPendingQueries(false)) ||
169 void GpuScheduler::SetSchedulingChangedCallback(
170 const SchedulingChangedCallback
& callback
) {
171 scheduling_changed_callback_
= callback
;
174 scoped_refptr
<Buffer
> GpuScheduler::GetSharedMemoryBuffer(int32 shm_id
) {
175 return command_buffer_
->GetTransferBuffer(shm_id
);
178 void GpuScheduler::set_token(int32 token
) {
179 command_buffer_
->SetToken(token
);
182 bool GpuScheduler::SetGetBuffer(int32 transfer_buffer_id
) {
183 scoped_refptr
<Buffer
> ring_buffer
=
184 command_buffer_
->GetTransferBuffer(transfer_buffer_id
);
185 if (!ring_buffer
.get()) {
189 if (!parser_
.get()) {
190 parser_
.reset(new CommandParser(handler_
));
194 ring_buffer
->memory(), ring_buffer
->size(), 0, ring_buffer
->size());
200 bool GpuScheduler::SetGetOffset(int32 offset
) {
201 if (parser_
->set_get(offset
)) {
202 command_buffer_
->SetGetOffset(static_cast<int32
>(parser_
->get()));
208 int32
GpuScheduler::GetGetOffset() {
209 return parser_
->get();
212 void GpuScheduler::SetCommandProcessedCallback(
213 const base::Closure
& callback
) {
214 command_processed_callback_
= callback
;
217 bool GpuScheduler::IsPreempted() {
218 if (!preemption_flag_
.get())
221 if (!was_preempted_
&& preemption_flag_
->IsSet()) {
222 TRACE_COUNTER_ID1("gpu", "GpuScheduler::Preempted", this, 1);
223 was_preempted_
= true;
224 } else if (was_preempted_
&& !preemption_flag_
->IsSet()) {
225 TRACE_COUNTER_ID1("gpu", "GpuScheduler::Preempted", this, 0);
226 was_preempted_
= false;
229 return preemption_flag_
->IsSet();
232 bool GpuScheduler::HasMoreIdleWork() {
233 return (decoder_
&& decoder_
->HasMoreIdleWork());
236 void GpuScheduler::PerformIdleWork() {
239 decoder_
->PerformIdleWork();
242 void GpuScheduler::RescheduleTimeOut() {
243 int new_count
= unscheduled_count_
+ rescheduled_count_
;
245 rescheduled_count_
= 0;
247 while (unscheduled_count_
)
250 rescheduled_count_
= new_count
;