summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/client/ring_buffer.cc
blob: 89da7fec2c1168a5dc14e77c0acb6909425b6dd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file contains the implementation of the RingBuffer class.

#include "../client/ring_buffer.h"
#include <algorithm>
#include "../client/cmd_buffer_helper.h"

namespace gpu {

RingBuffer::RingBuffer(
    Offset base_offset, unsigned int size, CommandBufferHelper* helper)
    : helper_(helper),
      base_offset_(base_offset),
      size_(size),
      free_offset_(0),
      in_use_offset_(0) {
}

RingBuffer::~RingBuffer() {
  // Free blocks pending tokens.
  while (!blocks_.empty()) {
    FreeOldestBlock();
  }
}

void RingBuffer::FreeOldestBlock() {
  DCHECK(!blocks_.empty()) << "no free blocks";
  Block& block = blocks_.front();
  DCHECK(block.valid) << "attempt to allocate more than maximum memory";
  helper_->WaitForToken(block.token);
  in_use_offset_ += block.size;
  if (in_use_offset_ == size_) {
    in_use_offset_ = 0;
  }
  // If they match then the entire buffer is free.
  if (in_use_offset_ == free_offset_) {
    in_use_offset_ = 0;
    free_offset_ = 0;
  }
  blocks_.pop_front();
}

RingBuffer::Offset RingBuffer::Alloc(unsigned int size) {
  DCHECK_LE(size, size_) << "attempt to allocate more than maximum memory";
  DCHECK(blocks_.empty() || blocks_.back().valid)
      << "Attempt to alloc another block before freeing the previous.";
  // Similarly to malloc, an allocation of 0 allocates at least 1 byte, to
  // return different pointers every time.
  if (size == 0) size = 1;

  // Wait until there is enough room.
  while (size > GetLargestFreeSizeNoWaiting()) {
    FreeOldestBlock();
  }

  Offset offset = free_offset_;
  blocks_.push_back(Block(offset, size));
  free_offset_ += size;
  if (free_offset_ == size_) {
    free_offset_ = 0;
  }
  return offset + base_offset_;
}

void RingBuffer::FreePendingToken(RingBuffer::Offset offset,
                                  unsigned int token) {
  offset -= base_offset_;
  DCHECK(!blocks_.empty()) << "no allocations to free";
  for (Container::reverse_iterator it = blocks_.rbegin();
        it != blocks_.rend();
        ++it) {
    Block& block = *it;
    if (block.offset == offset) {
      DCHECK(!block.valid) << "block that corresponds to offset already freed";
      block.token = token;
      block.valid = true;
      return;
    }
  }
  NOTREACHED() << "attempt to free non-existant block";
}

unsigned int RingBuffer::GetLargestFreeSizeNoWaiting() {
  // TODO(gman): Should check what the current token is and free up to that
  //    point.
  if (free_offset_ == in_use_offset_) {
    if (blocks_.empty()) {
      // The entire buffer is free.
      DCHECK_EQ(free_offset_, 0u);
      return size_;
    } else {
      // The entire buffer is in use.
      return 0;
    }
  } else if (free_offset_ > in_use_offset_) {
    // It's free from free_offset_ to size_
    return size_ - free_offset_;
  } else {
    // It's free from free_offset_ -> in_use_offset_;
    return in_use_offset_ - free_offset_;
  }
}

}  // namespace gpu