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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
gpu / command_buffer / client / ring_buffer.h [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
// This file contains the definition of the RingBuffer class.
#ifndef GPU_COMMAND_BUFFER_CLIENT_RING_BUFFER_H_
#define GPU_COMMAND_BUFFER_CLIENT_RING_BUFFER_H_
#include <stdint.h>
#include "base/containers/circular_deque.h"
#include "base/memory/raw_ptr.h"
#include "gpu/command_buffer/common/buffer.h"
#include "gpu/gpu_export.h"
namespace gpu {
class CommandBufferHelper;
// RingBuffer manages a piece of memory as a ring buffer. Memory is allocated
// with Alloc and then a is freed pending a token with FreePendingToken. Old
// allocations must not be kept past new allocations.
class GPU_EXPORT RingBuffer {
public:
typedef uint32_t Offset;
RingBuffer() = delete;
// Creates a RingBuffer.
// Parameters:
// buffer: the buffer that this ring buffer's data backing is based on.
// alignment: Alignment for allocations.
// base_offset: The offset of the start of the buffer.
// helper: A CommandBufferHelper for dealing with tokens.
RingBuffer(scoped_refptr<gpu::Buffer> buffer,
uint32_t alignment,
Offset base_offset,
CommandBufferHelper* helper);
RingBuffer(const RingBuffer&) = delete;
RingBuffer& operator=(const RingBuffer&) = delete;
~RingBuffer();
// Allocates a block of memory. If the buffer is out of directly available
// memory, this function may wait until memory that was freed "pending a
// token" can be re-used. The safest pattern of allocation is to only have
// one used allocation at once. Allocating while NumUsedBlocks > 0 can
// lead to deadlock if the entire buffer is exhausted. In this case, it is
// recommended to only Alloc smaller than GetFreeSizeNoWaiting.
//
// Parameters:
// size: the size of the memory block to allocate.
//
// Returns:
// the pointer to the allocated memory block.
void* Alloc(uint32_t size);
// Frees a block of memory, pending the passage of a token. That memory won't
// be re-allocated until the token has passed through the command stream.
// If a block is freed out of order, that hole will be counted as used
// in the Get*FreeSize* functions below.
//
// Parameters:
// pointer: the pointer to the memory block to free.
// token: the token value to wait for before re-using the memory.
void FreePendingToken(void* pointer, uint32_t token);
// Discards a block within the ring buffer.
//
// Parameters:
// pointer: the pointer to the memory block to free.
void DiscardBlock(void* pointer);
// Gets the size of the largest free block that is available without waiting.
uint32_t GetLargestFreeSizeNoWaiting();
// Gets the total size of all free blocks that are available without waiting.
uint32_t GetTotalFreeSizeNoWaiting();
// Gets the size of the largest free block that can be allocated if the
// caller can wait. Allocating a block of this size will succeed, but may
// block.
uint32_t GetLargestFreeOrPendingSize() {
// If size_ is not a multiple of alignment_, then trying to allocate it will
// cause us to try to allocate more than we actually can due to rounding up.
// So, round down here.
return size_ - size_ % alignment_;
}
// Total size minus usable size.
uint32_t GetUsedSize() { return size_ - GetLargestFreeSizeNoWaiting(); }
uint32_t NumUsedBlocks() const { return num_used_blocks_; }
// Gets a pointer to a memory block given the base memory and the offset.
void* GetPointer(RingBuffer::Offset offset) const {
return static_cast<int8_t*>(base_) + offset;
}
// Gets the offset to a memory block given the base memory and the address.
RingBuffer::Offset GetOffset(void* pointer) const {
return static_cast<int8_t*>(pointer) - static_cast<int8_t*>(base_);
}
// Rounds the given size to the alignment in use.
uint32_t RoundToAlignment(uint32_t size) {
return (size + alignment_ - 1) & ~(alignment_ - 1);
}
// Shrinks the last block. new_size must be smaller than the current size
// and the block must still be in use in order to shrink.
void ShrinkLastBlock(uint32_t new_size);
private:
enum State {
IN_USE,
PADDING,
FREE_PENDING_TOKEN
};
// Book-keeping sturcture that describes a block of memory.
struct Block {
Block(Offset _offset, uint32_t _size, State _state)
: offset(_offset), size(_size), token(0), state(_state) {}
Offset offset;
uint32_t size;
uint32_t token; // token to wait for.
State state;
};
using Container = base::circular_deque<Block>;
using BlockIndex = uint32_t;
void FreeOldestBlock();
uint32_t GetLargestFreeSizeNoWaitingInternal();
raw_ptr<CommandBufferHelper> helper_;
// Used blocks are added to the end, blocks are freed from the beginning.
Container blocks_;
// The buffer that this ring buffer's data backing is based on.
scoped_refptr<gpu::Buffer> buffer_;
// The base offset of the ring buffer.
Offset base_offset_;
// The size of the ring buffer.
Offset size_;
// Offset of first free byte.
Offset free_offset_ = 0;
// Offset of first used byte.
// Range between in_use_mark and free_mark is in use.
Offset in_use_offset_ = 0;
// Alignment for allocations.
uint32_t alignment_;
// Number of blocks in |blocks_| that are in the IN_USE state.
uint32_t num_used_blocks_ = 0;
// The physical address that corresponds to base_offset.
raw_ptr<void, AcrossTasksDanglingUntriaged> base_;
};
} // namespace gpu
#endif // GPU_COMMAND_BUFFER_CLIENT_RING_BUFFER_H_