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.
5 // A class to Manage a growing transfer buffer.
7 #include "gpu/command_buffer/client/transfer_buffer.h"
9 #include "base/logging.h"
10 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
14 AlignedRingBuffer::~AlignedRingBuffer() {
17 TransferBuffer::TransferBuffer(
18 CommandBufferHelper
* helper
)
21 default_buffer_size_(0),
26 bytes_since_last_flush_(0),
29 result_shm_offset_(0),
33 TransferBuffer::~TransferBuffer() {
37 bool TransferBuffer::Initialize(
38 unsigned int default_buffer_size
,
39 unsigned int result_size
,
40 unsigned int min_buffer_size
,
41 unsigned int max_buffer_size
,
42 unsigned int alignment
,
43 unsigned int size_to_flush
) {
44 result_size_
= result_size
;
45 default_buffer_size_
= default_buffer_size
;
46 min_buffer_size_
= min_buffer_size
;
47 max_buffer_size_
= max_buffer_size
;
48 alignment_
= alignment
;
49 size_to_flush_
= size_to_flush
;
50 ReallocateRingBuffer(default_buffer_size_
- result_size
);
54 void TransferBuffer::Free() {
57 helper_
->command_buffer()->DestroyTransferBuffer(buffer_id_
);
61 result_buffer_
= NULL
;
62 result_shm_offset_
= 0;
64 bytes_since_last_flush_
= 0;
68 bool TransferBuffer::HaveBuffer() const {
69 return buffer_id_
!= -1;
72 RingBuffer::Offset
TransferBuffer::GetOffset(void* pointer
) const {
73 return ring_buffer_
->GetOffset(pointer
);
76 void TransferBuffer::FreePendingToken(void* p
, unsigned int token
) {
77 ring_buffer_
->FreePendingToken(p
, token
);
78 if (bytes_since_last_flush_
>= size_to_flush_
&& size_to_flush_
> 0) {
80 bytes_since_last_flush_
= 0;
84 void TransferBuffer::AllocateRingBuffer(unsigned int size
) {
85 for (;size
>= min_buffer_size_
; size
/= 2) {
88 helper_
->command_buffer()->CreateTransferBuffer(size
, &id
);
91 ring_buffer_
.reset(new AlignedRingBuffer(
95 buffer_
.size
- result_size_
,
97 static_cast<char*>(buffer_
.ptr
) + result_size_
));
99 result_buffer_
= buffer_
.ptr
;
100 result_shm_offset_
= 0;
103 // we failed so don't try larger than this.
104 max_buffer_size_
= size
/ 2;
109 // Returns the integer i such as 2^i <= n < 2^(i+1)
110 static int Log2Floor(uint32 n
) {
115 for (int i
= 4; i
>= 0; --i
) {
116 int shift
= (1 << i
);
117 uint32 x
= value
>> shift
;
123 DCHECK_EQ(value
, 1u);
127 // Returns the integer i such as 2^(i-1) < n <= 2^i
128 static int Log2Ceiling(uint32 n
) {
132 // Log2Floor returns -1 for 0, so the following works correctly for n=1.
133 return 1 + Log2Floor(n
- 1);
137 static unsigned int ComputePOTSize(unsigned int dimension
) {
138 return (dimension
== 0) ? 0 : 1 << Log2Ceiling(dimension
);
141 void TransferBuffer::ReallocateRingBuffer(unsigned int size
) {
142 // What size buffer would we ask for if we needed a new one?
143 unsigned int needed_buffer_size
= ComputePOTSize(size
+ result_size_
);
144 needed_buffer_size
= std::max(needed_buffer_size
, min_buffer_size_
);
145 needed_buffer_size
= std::max(needed_buffer_size
, default_buffer_size_
);
146 needed_buffer_size
= std::min(needed_buffer_size
, max_buffer_size_
);
148 if (usable_
&& (!HaveBuffer() || needed_buffer_size
> buffer_
.size
)) {
152 AllocateRingBuffer(needed_buffer_size
);
156 void* TransferBuffer::AllocUpTo(
157 unsigned int size
, unsigned int* size_allocated
) {
158 DCHECK(size_allocated
);
160 ReallocateRingBuffer(size
);
166 unsigned int max_size
= ring_buffer_
->GetLargestFreeOrPendingSize();
167 *size_allocated
= std::min(max_size
, size
);
168 bytes_since_last_flush_
+= *size_allocated
;
169 return ring_buffer_
->Alloc(*size_allocated
);
172 void* TransferBuffer::Alloc(unsigned int size
) {
173 ReallocateRingBuffer(size
);
179 unsigned int max_size
= ring_buffer_
->GetLargestFreeOrPendingSize();
180 if (size
> max_size
) {
184 bytes_since_last_flush_
+= size
;
185 return ring_buffer_
->Alloc(size
);
188 void* TransferBuffer::GetResultBuffer() {
189 ReallocateRingBuffer(result_size_
);
190 return result_buffer_
;
193 int TransferBuffer::GetResultOffset() {
194 ReallocateRingBuffer(result_size_
);
195 return result_shm_offset_
;
198 int TransferBuffer::GetShmId() {
199 ReallocateRingBuffer(result_size_
);
203 unsigned int TransferBuffer::GetCurrentMaxAllocationWithoutRealloc() const {
204 return HaveBuffer() ? ring_buffer_
->GetLargestFreeOrPendingSize() : 0;
207 unsigned int TransferBuffer::GetMaxAllocation() const {
208 return HaveBuffer() ? max_buffer_size_
- result_size_
: 0;
211 void ScopedTransferBufferPtr::Release() {
213 transfer_buffer_
->FreePendingToken(buffer_
, helper_
->InsertToken());
219 void ScopedTransferBufferPtr::Reset(unsigned int new_size
) {
221 // NOTE: we allocate buffers of size 0 so that HaveBuffer will be true, so
222 // that address will return a pointer just like malloc, and so that GetShmId
223 // will be valid. That has the side effect that we'll insert a token on free.
224 // We could add code skip the token for a zero size buffer but it doesn't seem
225 // worth the complication.
226 buffer_
= transfer_buffer_
->AllocUpTo(new_size
, &size_
);