blob: 0304b8f7e5d1ae6d4322bdd889729477746a822c [file] [log] [blame]
[email protected]c4488402012-01-11 01:05:491// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]1bee3982009-12-17 23:15:282// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
[email protected]96449d2c2009-11-25 00:01:324
5// This file contains the implementation of the command buffer helper class.
6
[email protected]1df19862013-05-24 11:26:297#include "gpu/command_buffer/client/cmd_buffer_helper.h"
[email protected]cf1aa982013-11-05 21:49:378
avif15d60a2015-12-21 17:06:339#include <stdint.h>
10
martina.kollarovac640df12015-07-10 08:30:3811#include <algorithm>
[email protected]cf1aa982013-11-05 21:49:3712#include "base/logging.h"
reveman0cf65ee82015-08-25 22:15:2413#include "base/strings/stringprintf.h"
gabb23705312016-05-11 18:44:5614#include "base/threading/thread_task_runner_handle.h"
[email protected]da618e22014-07-11 09:27:1615#include "base/time/time.h"
reveman0cf65ee82015-08-25 22:15:2416#include "base/trace_event/memory_allocator_dump.h"
17#include "base/trace_event/memory_dump_manager.h"
18#include "base/trace_event/process_memory_dump.h"
martina.kollarovac0d13d12016-01-20 08:53:4419#include "base/trace_event/trace_event.h"
martina.kollarovac640df12015-07-10 08:30:3820#include "gpu/command_buffer/common/buffer.h"
[email protected]1df19862013-05-24 11:26:2921#include "gpu/command_buffer/common/command_buffer.h"
martina.kollarovac640df12015-07-10 08:30:3822#include "gpu/command_buffer/common/constants.h"
[email protected]96449d2c2009-11-25 00:01:3223
[email protected]a7a27ace2009-12-12 00:11:2524namespace gpu {
[email protected]96449d2c2009-11-25 00:01:3225
[email protected]96449d2c2009-11-25 00:01:3226CommandBufferHelper::CommandBufferHelper(CommandBuffer* command_buffer)
27 : command_buffer_(command_buffer),
[email protected]503b3a22011-12-12 23:29:4028 ring_buffer_id_(-1),
29 ring_buffer_size_(0),
[email protected]96449d2c2009-11-25 00:01:3230 entries_(NULL),
[email protected]9310b262010-06-03 16:15:4731 total_entry_count_(0),
[email protected]15691b42014-02-12 00:56:0032 immediate_entry_count_(0),
[email protected]96449d2c2009-11-25 00:01:3233 token_(0),
[email protected]7d5b8d12011-01-14 23:43:1534 put_(0),
[email protected]b36897c12011-07-12 18:04:1035 last_put_sent_(0),
[email protected]15691b42014-02-12 00:56:0036#if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
[email protected]b36897c12011-07-12 18:04:1037 commands_issued_(0),
[email protected]15691b42014-02-12 00:56:0038#endif
[email protected]c4488402012-01-11 01:05:4939 usable_(true),
[email protected]d35e6a72012-08-25 01:51:1340 context_lost_(false),
[email protected]362e6d602012-10-17 16:55:0641 flush_automatically_(true),
[email protected]3d668fb2014-02-22 00:49:3842 flush_generation_(0) {
reveman0cf65ee82015-08-25 22:15:2443 // In certain cases, ThreadTaskRunnerHandle isn't set (Android Webview).
44 // Don't register a dump provider in these cases.
45 // TODO(ericrk): Get this working in Android Webview. crbug.com/517156
46 if (base::ThreadTaskRunnerHandle::IsSet()) {
47 base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
primiano186d6bfe2015-10-30 13:21:4048 this, "gpu::CommandBufferHelper", base::ThreadTaskRunnerHandle::Get());
reveman0cf65ee82015-08-25 22:15:2449 }
[email protected]96449d2c2009-11-25 00:01:3250}
51
[email protected]362e6d602012-10-17 16:55:0652void CommandBufferHelper::SetAutomaticFlushes(bool enabled) {
53 flush_automatically_ = enabled;
[email protected]15691b42014-02-12 00:56:0054 CalcImmediateEntries(0);
[email protected]362e6d602012-10-17 16:55:0655}
56
[email protected]d35e6a72012-08-25 01:51:1357bool CommandBufferHelper::IsContextLost() {
58 if (!context_lost_) {
59 context_lost_ = error::IsError(command_buffer()->GetLastError());
60 }
61 return context_lost_;
62}
63
[email protected]15691b42014-02-12 00:56:0064void CommandBufferHelper::CalcImmediateEntries(int waiting_count) {
65 DCHECK_GE(waiting_count, 0);
66
67 // Check if usable & allocated.
68 if (!usable() || !HaveRingBuffer()) {
69 immediate_entry_count_ = 0;
70 return;
71 }
72
73 // Get maximum safe contiguous entries.
avif15d60a2015-12-21 17:06:3374 const int32_t curr_get = get_offset();
[email protected]15691b42014-02-12 00:56:0075 if (curr_get > put_) {
76 immediate_entry_count_ = curr_get - put_ - 1;
77 } else {
78 immediate_entry_count_ =
79 total_entry_count_ - put_ - (curr_get == 0 ? 1 : 0);
80 }
81
82 // Limit entry count to force early flushing.
83 if (flush_automatically_) {
avif15d60a2015-12-21 17:06:3384 int32_t limit =
[email protected]15691b42014-02-12 00:56:0085 total_entry_count_ /
86 ((curr_get == last_put_sent_) ? kAutoFlushSmall : kAutoFlushBig);
87
avif15d60a2015-12-21 17:06:3388 int32_t pending =
[email protected]15691b42014-02-12 00:56:0089 (put_ + total_entry_count_ - last_put_sent_) % total_entry_count_;
90
91 if (pending > 0 && pending >= limit) {
92 // Time to force flush.
93 immediate_entry_count_ = 0;
94 } else {
95 // Limit remaining entries, but not lower than waiting_count entries to
96 // prevent deadlock when command size is greater than the flush limit.
97 limit -= pending;
98 limit = limit < waiting_count ? waiting_count : limit;
99 immediate_entry_count_ =
100 immediate_entry_count_ > limit ? limit : immediate_entry_count_;
101 }
102 }
103}
104
[email protected]503b3a22011-12-12 23:29:40105bool CommandBufferHelper::AllocateRingBuffer() {
[email protected]c4488402012-01-11 01:05:49106 if (!usable()) {
107 return false;
108 }
109
[email protected]617296e2011-12-15 05:37:57110 if (HaveRingBuffer()) {
111 return true;
112 }
113
avif15d60a2015-12-21 17:06:33114 int32_t id = -1;
[email protected]44096602014-03-26 04:53:58115 scoped_refptr<Buffer> buffer =
116 command_buffer_->CreateTransferBuffer(ring_buffer_size_, &id);
[email protected]503b3a22011-12-12 23:29:40117 if (id < 0) {
[email protected]c4488402012-01-11 01:05:49118 ClearUsable();
dyena6d4e172015-08-04 01:17:05119 DCHECK(error::IsError(command_buffer()->GetLastError()));
[email protected]503b3a22011-12-12 23:29:40120 return false;
121 }
122
[email protected]67c80782012-12-21 01:16:52123 ring_buffer_ = buffer;
[email protected]503b3a22011-12-12 23:29:40124 ring_buffer_id_ = id;
125 command_buffer_->SetGetBuffer(id);
[email protected]44096602014-03-26 04:53:58126 entries_ = static_cast<CommandBufferEntry*>(ring_buffer_->memory());
[email protected]bae23772014-04-16 09:50:55127 total_entry_count_ = ring_buffer_size_ / sizeof(CommandBufferEntry);
128 // Call to SetGetBuffer(id) above resets get and put offsets to 0.
129 // No need to query it through IPC.
130 put_ = 0;
[email protected]15691b42014-02-12 00:56:00131 CalcImmediateEntries(0);
[email protected]96449d2c2009-11-25 00:01:32132 return true;
133}
134
[email protected]a5cf3cb2012-08-23 01:08:42135void CommandBufferHelper::FreeResources() {
[email protected]617296e2011-12-15 05:37:57136 if (HaveRingBuffer()) {
137 command_buffer_->DestroyTransferBuffer(ring_buffer_id_);
138 ring_buffer_id_ = -1;
[email protected]15691b42014-02-12 00:56:00139 CalcImmediateEntries(0);
boliuada52452015-07-29 21:42:25140 entries_ = nullptr;
141 ring_buffer_ = nullptr;
[email protected]617296e2011-12-15 05:37:57142 }
143}
144
[email protected]a5cf3cb2012-08-23 01:08:42145void CommandBufferHelper::FreeRingBuffer() {
[email protected]cf1aa982013-11-05 21:49:37146 CHECK((put_ == get_offset()) ||
[email protected]ea51dbd2013-01-18 10:03:53147 error::IsError(command_buffer_->GetLastState().error));
[email protected]a5cf3cb2012-08-23 01:08:42148 FreeResources();
149}
150
avif15d60a2015-12-21 17:06:33151bool CommandBufferHelper::Initialize(int32_t ring_buffer_size) {
[email protected]503b3a22011-12-12 23:29:40152 ring_buffer_size_ = ring_buffer_size;
153 return AllocateRingBuffer();
154}
155
[email protected]96449d2c2009-11-25 00:01:32156CommandBufferHelper::~CommandBufferHelper() {
reveman0cf65ee82015-08-25 22:15:24157 base::trace_event::MemoryDumpManager::GetInstance()->UnregisterDumpProvider(
158 this);
[email protected]a5cf3cb2012-08-23 01:08:42159 FreeResources();
[email protected]96449d2c2009-11-25 00:01:32160}
161
avif15d60a2015-12-21 17:06:33162bool CommandBufferHelper::WaitForGetOffsetInRange(int32_t start, int32_t end) {
vmiura926192c2015-12-11 20:10:03163 DCHECK(start >= 0 && start <= total_entry_count_);
164 DCHECK(end >= 0 && end <= total_entry_count_);
[email protected]c4488402012-01-11 01:05:49165 if (!usable()) {
166 return false;
167 }
[email protected]7fe4198b2014-03-18 21:52:36168 command_buffer_->WaitForGetOffsetInRange(start, end);
169 return command_buffer_->GetLastError() == gpu::error::kNoError;
[email protected]96449d2c2009-11-25 00:01:32170}
171
[email protected]7d5b8d12011-01-14 23:43:15172void CommandBufferHelper::Flush() {
[email protected]15691b42014-02-12 00:56:00173 // Wrap put_ before flush.
174 if (put_ == total_entry_count_)
175 put_ = 0;
176
vmiurab700b432015-02-06 16:42:51177 if (usable()) {
[email protected]da618e22014-07-11 09:27:16178 last_flush_time_ = base::TimeTicks::Now();
[email protected]c4488402012-01-11 01:05:49179 last_put_sent_ = put_;
180 command_buffer_->Flush(put_);
[email protected]cbe0ded2014-02-21 20:42:52181 ++flush_generation_;
[email protected]15691b42014-02-12 00:56:00182 CalcImmediateEntries(0);
[email protected]c4488402012-01-11 01:05:49183 }
[email protected]7d5b8d12011-01-14 23:43:15184}
185
vmiurab700b432015-02-06 16:42:51186void CommandBufferHelper::OrderingBarrier() {
187 // Wrap put_ before setting the barrier.
188 if (put_ == total_entry_count_)
189 put_ = 0;
190
191 if (usable()) {
192 command_buffer_->OrderingBarrier(put_);
193 ++flush_generation_;
194 CalcImmediateEntries(0);
195 }
196}
197
[email protected]15691b42014-02-12 00:56:00198#if defined(CMD_HELPER_PERIODIC_FLUSH_CHECK)
199void CommandBufferHelper::PeriodicFlushCheck() {
[email protected]da618e22014-07-11 09:27:16200 base::TimeTicks current_time = base::TimeTicks::Now();
201 if (current_time - last_flush_time_ >
202 base::TimeDelta::FromMicroseconds(kPeriodicFlushDelayInMicroseconds)) {
[email protected]15691b42014-02-12 00:56:00203 Flush();
[email protected]da618e22014-07-11 09:27:16204 }
[email protected]15691b42014-02-12 00:56:00205}
206#endif
207
[email protected]96449d2c2009-11-25 00:01:32208// Calls Flush() and then waits until the buffer is empty. Break early if the
209// error is set.
210bool CommandBufferHelper::Finish() {
[email protected]366ae242011-05-10 02:23:58211 TRACE_EVENT0("gpu", "CommandBufferHelper::Finish");
[email protected]c4488402012-01-11 01:05:49212 if (!usable()) {
213 return false;
214 }
[email protected]a24e7582012-02-15 23:21:32215 // If there is no work just exit.
216 if (put_ == get_offset()) {
217 return true;
218 }
reveman91d23272015-08-20 13:41:38219 DCHECK(HaveRingBuffer() ||
220 error::IsError(command_buffer_->GetLastState().error));
[email protected]7fe4198b2014-03-18 21:52:36221 Flush();
222 if (!WaitForGetOffsetInRange(put_, put_))
223 return false;
224 DCHECK_EQ(get_offset(), put_);
225
226 CalcImmediateEntries(0);
[email protected]96449d2c2009-11-25 00:01:32227
228 return true;
229}
230
231// Inserts a new token into the command stream. It uses an increasing value
232// scheme so that we don't lose tokens (a token has passed if the current token
233// value is higher than that token). Calls Finish() if the token value wraps,
234// which will be rare.
avif15d60a2015-12-21 17:06:33235int32_t CommandBufferHelper::InsertToken() {
[email protected]617296e2011-12-15 05:37:57236 AllocateRingBuffer();
[email protected]c4488402012-01-11 01:05:49237 if (!usable()) {
238 return token_;
239 }
[email protected]cf1aa982013-11-05 21:49:37240 DCHECK(HaveRingBuffer());
[email protected]96449d2c2009-11-25 00:01:32241 // Increment token as 31-bit integer. Negative values are used to signal an
242 // error.
243 token_ = (token_ + 1) & 0x7FFFFFFF;
[email protected]c4488402012-01-11 01:05:49244 cmd::SetToken* cmd = GetCmdSpace<cmd::SetToken>();
245 if (cmd) {
246 cmd->Init(token_);
247 if (token_ == 0) {
248 TRACE_EVENT0("gpu", "CommandBufferHelper::InsertToken(wrapped)");
249 // we wrapped
250 Finish();
[email protected]cf1aa982013-11-05 21:49:37251 DCHECK_EQ(token_, last_token_read());
[email protected]c4488402012-01-11 01:05:49252 }
[email protected]96449d2c2009-11-25 00:01:32253 }
254 return token_;
255}
256
257// Waits until the current token value is greater or equal to the value passed
258// in argument.
avif15d60a2015-12-21 17:06:33259void CommandBufferHelper::WaitForToken(int32_t token) {
[email protected]ea51dbd2013-01-18 10:03:53260 if (!usable() || !HaveRingBuffer()) {
[email protected]c4488402012-01-11 01:05:49261 return;
262 }
[email protected]96449d2c2009-11-25 00:01:32263 // Return immediately if corresponding InsertToken failed.
264 if (token < 0)
265 return;
[email protected]96449d2c2009-11-25 00:01:32266 if (token > token_) return; // we wrapped
[email protected]03e9c1d2014-05-06 18:00:33267 if (last_token_read() >= token)
[email protected]7fe4198b2014-03-18 21:52:36268 return;
269 Flush();
270 command_buffer_->WaitForTokenInRange(token, token_);
[email protected]96449d2c2009-11-25 00:01:32271}
272
273// Waits for available entries, basically waiting until get >= put + count + 1.
274// It actually waits for contiguous entries, so it may need to wrap the buffer
[email protected]47257372013-01-04 18:37:48275// around, adding a noops. Thus this function may change the value of put_. The
[email protected]9310b262010-06-03 16:15:47276// function will return early if an error occurs, in which case the available
277// space may not be available.
avif15d60a2015-12-21 17:06:33278void CommandBufferHelper::WaitForAvailableEntries(int32_t count) {
[email protected]3110b122013-11-19 23:25:54279 AllocateRingBuffer();
280 if (!usable()) {
281 return;
282 }
283 DCHECK(HaveRingBuffer());
[email protected]cf1aa982013-11-05 21:49:37284 DCHECK(count < total_entry_count_);
[email protected]47257372013-01-04 18:37:48285 if (put_ + count > total_entry_count_) {
[email protected]96449d2c2009-11-25 00:01:32286 // There's not enough room between the current put and the end of the
[email protected]47257372013-01-04 18:37:48287 // buffer, so we need to wrap. We will add noops all the way to the end,
288 // but we need to make sure get wraps first, actually that get is 1 or
289 // more (since put will wrap to 0 after we add the noops).
[email protected]cf1aa982013-11-05 21:49:37290 DCHECK_LE(1, put_);
avif15d60a2015-12-21 17:06:33291 int32_t curr_get = get_offset();
[email protected]15691b42014-02-12 00:56:00292 if (curr_get > put_ || curr_get == 0) {
[email protected]366ae242011-05-10 02:23:58293 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries");
[email protected]7fe4198b2014-03-18 21:52:36294 Flush();
295 if (!WaitForGetOffsetInRange(1, put_))
296 return;
297 curr_get = get_offset();
298 DCHECK_LE(curr_get, put_);
299 DCHECK_NE(0, curr_get);
[email protected]96449d2c2009-11-25 00:01:32300 }
[email protected]47257372013-01-04 18:37:48301 // Insert Noops to fill out the buffer.
avif15d60a2015-12-21 17:06:33302 int32_t num_entries = total_entry_count_ - put_;
[email protected]47257372013-01-04 18:37:48303 while (num_entries > 0) {
avif15d60a2015-12-21 17:06:33304 int32_t num_to_skip = std::min(CommandHeader::kMaxSize, num_entries);
[email protected]47257372013-01-04 18:37:48305 cmd::Noop::Set(&entries_[put_], num_to_skip);
306 put_ += num_to_skip;
307 num_entries -= num_to_skip;
308 }
[email protected]96449d2c2009-11-25 00:01:32309 put_ = 0;
310 }
[email protected]15691b42014-02-12 00:56:00311
312 // Try to get 'count' entries without flushing.
313 CalcImmediateEntries(count);
314 if (immediate_entry_count_ < count) {
315 // Try again with a shallow Flush().
[email protected]3110b122013-11-19 23:25:54316 Flush();
[email protected]15691b42014-02-12 00:56:00317 CalcImmediateEntries(count);
318 if (immediate_entry_count_ < count) {
319 // Buffer is full. Need to wait for entries.
320 TRACE_EVENT0("gpu", "CommandBufferHelper::WaitForAvailableEntries1");
vmiura926192c2015-12-11 20:10:03321 if (!WaitForGetOffsetInRange((put_ + count + 1) % total_entry_count_,
322 put_))
[email protected]7fe4198b2014-03-18 21:52:36323 return;
324 CalcImmediateEntries(count);
325 DCHECK_GE(immediate_entry_count_, count);
[email protected]15691b42014-02-12 00:56:00326 }
[email protected]3110b122013-11-19 23:25:54327 }
[email protected]96449d2c2009-11-25 00:01:32328}
329
avif15d60a2015-12-21 17:06:33330int32_t CommandBufferHelper::GetTotalFreeEntriesNoWaiting() const {
331 int32_t current_get_offset = get_offset();
reveman0cf65ee82015-08-25 22:15:24332 if (current_get_offset > put_) {
333 return current_get_offset - put_ - 1;
334 } else {
335 return current_get_offset + total_entry_count_ - put_ -
336 (current_get_offset == 0 ? 1 : 0);
337 }
338}
339
340bool CommandBufferHelper::OnMemoryDump(
341 const base::trace_event::MemoryDumpArgs& args,
342 base::trace_event::ProcessMemoryDump* pmd) {
ericrkeff776982016-11-03 21:37:31343 using base::trace_event::MemoryAllocatorDump;
344 using base::trace_event::MemoryDumpLevelOfDetail;
345
reveman0cf65ee82015-08-25 22:15:24346 if (!HaveRingBuffer())
347 return true;
348
avif15d60a2015-12-21 17:06:33349 const uint64_t tracing_process_id =
reveman0cf65ee82015-08-25 22:15:24350 base::trace_event::MemoryDumpManager::GetInstance()
351 ->GetTracingProcessId();
352
ericrkeff776982016-11-03 21:37:31353 MemoryAllocatorDump* dump = pmd->CreateAllocatorDump(base::StringPrintf(
354 "gpu/command_buffer_memory/buffer_%d", ring_buffer_id_));
355 dump->AddScalar(MemoryAllocatorDump::kNameSize,
356 MemoryAllocatorDump::kUnitsBytes, ring_buffer_size_);
357
358 if (args.level_of_detail != MemoryDumpLevelOfDetail::BACKGROUND) {
359 dump->AddScalar(
360 "free_size", MemoryAllocatorDump::kUnitsBytes,
361 GetTotalFreeEntriesNoWaiting() * sizeof(CommandBufferEntry));
362 auto guid = GetBufferGUIDForTracing(tracing_process_id, ring_buffer_id_);
363 const int kImportance = 2;
364 pmd->CreateSharedGlobalAllocatorDump(guid);
365 pmd->AddOwnershipEdge(dump->guid(), guid, kImportance);
366 }
reveman0cf65ee82015-08-25 22:15:24367
368 return true;
369}
[email protected]96449d2c2009-11-25 00:01:32370
[email protected]a7a27ace2009-12-12 00:11:25371} // namespace gpu