Home | History | Annotate | Download | only in service
      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(CommandBuffer* 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       reschedule_task_factory_(this),
     40       was_preempted_(false) {}
     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_->GetState();
     51 
     52   // If there is no parser, exit.
     53   if (!parser_.get()) {
     54     DCHECK_EQ(state.get_offset, state.put_offset);
     55     return;
     56   }
     57 
     58   parser_->set_put(state.put_offset);
     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   while (!parser_->IsEmpty()) {
     73     if (IsPreempted())
     74       break;
     75 
     76     DCHECK(IsScheduled());
     77     DCHECK(unschedule_fences_.empty());
     78 
     79     error = parser_->ProcessCommand();
     80 
     81     if (error == error::kDeferCommandUntilLater) {
     82       DCHECK_GT(unscheduled_count_, 0);
     83       break;
     84     }
     85 
     86     // TODO(piman): various classes duplicate various pieces of state, leading
     87     // to needlessly complex update logic. It should be possible to simply
     88     // share the state across all of them.
     89     command_buffer_->SetGetOffset(static_cast<int32>(parser_->get()));
     90 
     91     if (error::IsError(error)) {
     92       LOG(ERROR) << "[" << decoder_ << "] "
     93                  << "GPU PARSE ERROR: " << 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_->AddProcessingCommandsTime(
    112         base::TimeTicks::HighResNow() - begin_time);
    113   }
    114 }
    115 
    116 void GpuScheduler::SetScheduled(bool scheduled) {
    117   TRACE_EVENT2("gpu", "GpuScheduler:SetScheduled", "this", this,
    118                "new unscheduled_count_",
    119                unscheduled_count_ + (scheduled? -1 : 1));
    120   if (scheduled) {
    121     // If the scheduler was rescheduled after a timeout, ignore the subsequent
    122     // calls to SetScheduled when they eventually arrive until they are all
    123     // accounted for.
    124     if (rescheduled_count_ > 0) {
    125       --rescheduled_count_;
    126       return;
    127     } else {
    128       --unscheduled_count_;
    129     }
    130 
    131     DCHECK_GE(unscheduled_count_, 0);
    132 
    133     if (unscheduled_count_ == 0) {
    134       TRACE_EVENT_ASYNC_END1("gpu", "ProcessingSwap", this,
    135                              "GpuScheduler", this);
    136       // When the scheduler transitions from the unscheduled to the scheduled
    137       // state, cancel the task that would reschedule it after a timeout.
    138       reschedule_task_factory_.InvalidateWeakPtrs();
    139 
    140       if (!scheduling_changed_callback_.is_null())
    141         scheduling_changed_callback_.Run(true);
    142     }
    143   } else {
    144     ++unscheduled_count_;
    145     if (unscheduled_count_ == 1) {
    146       TRACE_EVENT_ASYNC_BEGIN1("gpu", "ProcessingSwap", this,
    147                                "GpuScheduler", this);
    148 #if defined(OS_WIN)
    149       if (base::win::GetVersion() < base::win::VERSION_VISTA) {
    150         // When the scheduler transitions from scheduled to unscheduled, post a
    151         // delayed task that it will force it back into a scheduled state after
    152         // a timeout. This should only be necessary on pre-Vista.
    153         base::MessageLoop::current()->PostDelayedTask(
    154             FROM_HERE,
    155             base::Bind(&GpuScheduler::RescheduleTimeOut,
    156                        reschedule_task_factory_.GetWeakPtr()),
    157             base::TimeDelta::FromMilliseconds(kRescheduleTimeOutDelay));
    158       }
    159 #endif
    160       if (!scheduling_changed_callback_.is_null())
    161         scheduling_changed_callback_.Run(false);
    162     }
    163   }
    164 }
    165 
    166 bool GpuScheduler::IsScheduled() {
    167   return unscheduled_count_ == 0;
    168 }
    169 
    170 bool GpuScheduler::HasMoreWork() {
    171   return !unschedule_fences_.empty() ||
    172          (decoder_ && decoder_->ProcessPendingQueries()) ||
    173          HasMoreIdleWork();
    174 }
    175 
    176 void GpuScheduler::SetSchedulingChangedCallback(
    177     const SchedulingChangedCallback& callback) {
    178   scheduling_changed_callback_ = callback;
    179 }
    180 
    181 Buffer GpuScheduler::GetSharedMemoryBuffer(int32 shm_id) {
    182   return command_buffer_->GetTransferBuffer(shm_id);
    183 }
    184 
    185 void GpuScheduler::set_token(int32 token) {
    186   command_buffer_->SetToken(token);
    187 }
    188 
    189 bool GpuScheduler::SetGetBuffer(int32 transfer_buffer_id) {
    190   Buffer ring_buffer = command_buffer_->GetTransferBuffer(transfer_buffer_id);
    191   if (!ring_buffer.ptr) {
    192     return false;
    193   }
    194 
    195   if (!parser_.get()) {
    196     parser_.reset(new CommandParser(handler_));
    197   }
    198 
    199   parser_->SetBuffer(
    200       ring_buffer.ptr,
    201       ring_buffer.size,
    202       0,
    203       ring_buffer.size);
    204 
    205   SetGetOffset(0);
    206   return true;
    207 }
    208 
    209 bool GpuScheduler::SetGetOffset(int32 offset) {
    210   if (parser_->set_get(offset)) {
    211     command_buffer_->SetGetOffset(static_cast<int32>(parser_->get()));
    212     return true;
    213   }
    214   return false;
    215 }
    216 
    217 int32 GpuScheduler::GetGetOffset() {
    218   return parser_->get();
    219 }
    220 
    221 void GpuScheduler::SetCommandProcessedCallback(
    222     const base::Closure& callback) {
    223   command_processed_callback_ = callback;
    224 }
    225 
    226 void GpuScheduler::DeferToFence(base::Closure task) {
    227   unschedule_fences_.push(make_linked_ptr(
    228        new UnscheduleFence(gfx::GLFence::Create(), task)));
    229   SetScheduled(false);
    230 }
    231 
    232 bool GpuScheduler::PollUnscheduleFences() {
    233   if (unschedule_fences_.empty())
    234     return true;
    235 
    236   if (unschedule_fences_.front()->fence.get()) {
    237     base::Time now = base::Time::Now();
    238     base::TimeDelta timeout =
    239         base::TimeDelta::FromMilliseconds(kUnscheduleFenceTimeOutDelay);
    240 
    241     while (!unschedule_fences_.empty()) {
    242       const UnscheduleFence& fence = *unschedule_fences_.front();
    243       if (fence.fence->HasCompleted() ||
    244           now - fence.issue_time > timeout) {
    245         unschedule_fences_.front()->task.Run();
    246         unschedule_fences_.pop();
    247         SetScheduled(true);
    248       } else {
    249         return false;
    250       }
    251     }
    252   } else {
    253     glFinish();
    254 
    255     while (!unschedule_fences_.empty()) {
    256       unschedule_fences_.front()->task.Run();
    257       unschedule_fences_.pop();
    258       SetScheduled(true);
    259     }
    260   }
    261 
    262   return true;
    263 }
    264 
    265 bool GpuScheduler::IsPreempted() {
    266   if (!preemption_flag_.get())
    267     return false;
    268 
    269   if (!was_preempted_ && preemption_flag_->IsSet()) {
    270     TRACE_COUNTER_ID1("gpu", "GpuScheduler::Preempted", this, 1);
    271     was_preempted_ = true;
    272   } else if (was_preempted_ && !preemption_flag_->IsSet()) {
    273     TRACE_COUNTER_ID1("gpu", "GpuScheduler::Preempted", this, 0);
    274     was_preempted_ = false;
    275   }
    276 
    277   return preemption_flag_->IsSet();
    278 }
    279 
    280 bool GpuScheduler::HasMoreIdleWork() {
    281   return (decoder_ && decoder_->HasMoreIdleWork());
    282 }
    283 
    284 void GpuScheduler::PerformIdleWork() {
    285   if (!decoder_)
    286     return;
    287   decoder_->PerformIdleWork();
    288 }
    289 
    290 void GpuScheduler::RescheduleTimeOut() {
    291   int new_count = unscheduled_count_ + rescheduled_count_;
    292 
    293   rescheduled_count_ = 0;
    294 
    295   while (unscheduled_count_)
    296     SetScheduled(true);
    297 
    298   rescheduled_count_ = new_count;
    299 }
    300 
    301 GpuScheduler::UnscheduleFence::UnscheduleFence(gfx::GLFence* fence_,
    302                                                base::Closure task_)
    303   : fence(fence_),
    304     issue_time(base::Time::Now()),
    305     task(task_) {
    306 }
    307 
    308 GpuScheduler::UnscheduleFence::~UnscheduleFence() {
    309 }
    310 
    311 }  // namespace gpu
    312