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// This file contains the definition of the FencedAllocator class.
6
7#ifndef GPU_COMMAND_BUFFER_CLIENT_FENCED_ALLOCATOR_H_
8#define GPU_COMMAND_BUFFER_CLIENT_FENCED_ALLOCATOR_H_
9
10#include <stdint.h>
11
12#include <vector>
13
14#include "base/bind.h"
15#include "base/logging.h"
16#include "base/macros.h"
17#include "gpu/gpu_export.h"
18
19namespace gpu {
20class CommandBufferHelper;
21
22// FencedAllocator provides a mechanism to manage allocations within a fixed
23// block of memory (storing the book-keeping externally). Furthermore this
24// class allows to free data "pending" the passage of a command buffer token,
25// that is, the memory won't be reused until the command buffer has processed
26// that token.
27//
28// NOTE: Although this class is intended to be used in the command buffer
29// environment which is multi-process, this class isn't "thread safe", because
30// it isn't meant to be shared across modules. It is thread-compatible though
31// (see http://www.corp.google.com/eng/doc/cpp_primer.html#thread_safety).
32class GPU_EXPORT FencedAllocator {
33 public:
34  typedef unsigned int Offset;
35  // Invalid offset, returned by Alloc in case of failure.
36  static const Offset kInvalidOffset = 0xffffffffU;
37
38  // Creates a FencedAllocator. Note that the size of the buffer is passed, but
39  // not its base address: everything is handled as offsets into the buffer.
40  FencedAllocator(unsigned int size,
41                  CommandBufferHelper *helper,
42                  const base::Closure& poll_callback);
43
44  ~FencedAllocator();
45
46  // Allocates a block of memory. If the buffer is out of directly available
47  // memory, this function may wait until memory that was freed "pending a
48  // token" can be re-used.
49  //
50  // Parameters:
51  //   size: the size of the memory block to allocate.
52  //
53  // Returns:
54  //   the offset of the allocated memory block, or kInvalidOffset if out of
55  //   memory.
56  Offset Alloc(unsigned int size);
57
58  // Frees a block of memory.
59  //
60  // Parameters:
61  //   offset: the offset of the memory block to free.
62  void Free(Offset offset);
63
64  // Frees a block of memory, pending the passage of a token. That memory won't
65  // be re-allocated until the token has passed through the command stream.
66  //
67  // Parameters:
68  //   offset: the offset of the memory block to free.
69  //   token: the token value to wait for before re-using the memory.
70  void FreePendingToken(Offset offset, int32 token);
71
72  // Frees any blocks pending a token for which the token has been read.
73  void FreeUnused();
74
75  // Gets the size of the largest free block that is available without waiting.
76  unsigned int GetLargestFreeSize();
77
78  // Gets the size of the largest free block that can be allocated if the
79  // caller can wait. Allocating a block of this size will succeed, but may
80  // block.
81  unsigned int GetLargestFreeOrPendingSize();
82
83  // Checks for consistency inside the book-keeping structures. Used for
84  // testing.
85  bool CheckConsistency();
86
87  // True if any memory is allocated.
88  bool InUse();
89
90  // Return bytes of memory that is IN_USE
91  size_t bytes_in_use() const { return bytes_in_use_; }
92
93 private:
94  // Status of a block of memory, for book-keeping.
95  enum State {
96    IN_USE,
97    FREE,
98    FREE_PENDING_TOKEN
99  };
100
101  // Book-keeping sturcture that describes a block of memory.
102  struct Block {
103    State state;
104    Offset offset;
105    unsigned int size;
106    int32_t token;  // token to wait for in the FREE_PENDING_TOKEN case.
107  };
108
109  // Comparison functor for memory block sorting.
110  class OffsetCmp {
111   public:
112    bool operator() (const Block &left, const Block &right) {
113      return left.offset < right.offset;
114    }
115  };
116
117  typedef std::vector<Block> Container;
118  typedef unsigned int BlockIndex;
119
120  static const int32_t kUnusedToken = 0;
121
122  // Gets the index of a memory block, given its offset.
123  BlockIndex GetBlockByOffset(Offset offset);
124
125  // Collapse a free block with its neighbours if they are free. Returns the
126  // index of the collapsed block.
127  // NOTE: this will invalidate block indices.
128  BlockIndex CollapseFreeBlock(BlockIndex index);
129
130  // Waits for a FREE_PENDING_TOKEN block to be usable, and free it. Returns
131  // the new index of that block (since it may have been collapsed).
132  // NOTE: this will invalidate block indices.
133  BlockIndex WaitForTokenAndFreeBlock(BlockIndex index);
134
135  // Allocates a block of memory inside a given block, splitting it in two
136  // (unless that block is of the exact requested size).
137  // NOTE: this will invalidate block indices.
138  // Returns the offset of the allocated block (NOTE: this is different from
139  // the other functions that return a block index).
140  Offset AllocInBlock(BlockIndex index, unsigned int size);
141
142  CommandBufferHelper *helper_;
143  base::Closure poll_callback_;
144  Container blocks_;
145  size_t bytes_in_use_;
146
147  DISALLOW_IMPLICIT_CONSTRUCTORS(FencedAllocator);
148};
149
150// This class functions just like FencedAllocator, but its API uses pointers
151// instead of offsets.
152class FencedAllocatorWrapper {
153 public:
154  FencedAllocatorWrapper(unsigned int size,
155                         CommandBufferHelper* helper,
156                         const base::Closure& poll_callback,
157                         void* base)
158      : allocator_(size, helper, poll_callback),
159        base_(base) { }
160
161  // Allocates a block of memory. If the buffer is out of directly available
162  // memory, this function may wait until memory that was freed "pending a
163  // token" can be re-used.
164  //
165  // Parameters:
166  //   size: the size of the memory block to allocate.
167  //
168  // Returns:
169  //   the pointer to the allocated memory block, or NULL if out of
170  //   memory.
171  void *Alloc(unsigned int size) {
172    FencedAllocator::Offset offset = allocator_.Alloc(size);
173    return GetPointer(offset);
174  }
175
176  // Allocates a block of memory. If the buffer is out of directly available
177  // memory, this function may wait until memory that was freed "pending a
178  // token" can be re-used.
179  // This is a type-safe version of Alloc, returning a typed pointer.
180  //
181  // Parameters:
182  //   count: the number of elements to allocate.
183  //
184  // Returns:
185  //   the pointer to the allocated memory block, or NULL if out of
186  //   memory.
187  template <typename T> T *AllocTyped(unsigned int count) {
188    return static_cast<T *>(Alloc(count * sizeof(T)));
189  }
190
191  // Frees a block of memory.
192  //
193  // Parameters:
194  //   pointer: the pointer to the memory block to free.
195  void Free(void *pointer) {
196    DCHECK(pointer);
197    allocator_.Free(GetOffset(pointer));
198  }
199
200  // Frees a block of memory, pending the passage of a token. That memory won't
201  // be re-allocated until the token has passed through the command stream.
202  //
203  // Parameters:
204  //   pointer: the pointer to the memory block to free.
205  //   token: the token value to wait for before re-using the memory.
206  void FreePendingToken(void *pointer, int32 token) {
207    DCHECK(pointer);
208    allocator_.FreePendingToken(GetOffset(pointer), token);
209  }
210
211  // Frees any blocks pending a token for which the token has been read.
212  void FreeUnused() {
213    allocator_.FreeUnused();
214  }
215
216  // Gets a pointer to a memory block given the base memory and the offset.
217  // It translates FencedAllocator::kInvalidOffset to NULL.
218  void *GetPointer(FencedAllocator::Offset offset) {
219    return (offset == FencedAllocator::kInvalidOffset) ?
220        NULL : static_cast<char *>(base_) + offset;
221  }
222
223  // Gets the offset to a memory block given the base memory and the address.
224  // It translates NULL to FencedAllocator::kInvalidOffset.
225  FencedAllocator::Offset GetOffset(void *pointer) {
226    return pointer ?
227        static_cast<FencedAllocator::Offset>(
228            static_cast<char*>(pointer) - static_cast<char*>(base_)) :
229        FencedAllocator::kInvalidOffset;
230  }
231
232  // Gets the size of the largest free block that is available without waiting.
233  unsigned int GetLargestFreeSize() {
234    return allocator_.GetLargestFreeSize();
235  }
236
237  // Gets the size of the largest free block that can be allocated if the
238  // caller can wait.
239  unsigned int GetLargestFreeOrPendingSize() {
240    return allocator_.GetLargestFreeOrPendingSize();
241  }
242
243  // Checks for consistency inside the book-keeping structures. Used for
244  // testing.
245  bool CheckConsistency() {
246    return allocator_.CheckConsistency();
247  }
248
249  // True if any memory is allocated.
250  bool InUse() {
251    return allocator_.InUse();
252  }
253
254  FencedAllocator &allocator() { return allocator_; }
255
256  size_t bytes_in_use() const { return allocator_.bytes_in_use(); }
257
258 private:
259  FencedAllocator allocator_;
260  void* base_;
261  DISALLOW_IMPLICIT_CONSTRUCTORS(FencedAllocatorWrapper);
262};
263
264}  // namespace gpu
265
266#endif  // GPU_COMMAND_BUFFER_CLIENT_FENCED_ALLOCATOR_H_
267