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"
8 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
12 AlignedRingBuffer::~AlignedRingBuffer() {
15 TransferBuffer::TransferBuffer(
16 CommandBufferHelper
* helper
)
19 default_buffer_size_(0),
24 bytes_since_last_flush_(0),
27 result_shm_offset_(0),
31 TransferBuffer::~TransferBuffer() {
35 bool TransferBuffer::Initialize(
36 unsigned int default_buffer_size
,
37 unsigned int result_size
,
38 unsigned int min_buffer_size
,
39 unsigned int max_buffer_size
,
40 unsigned int alignment
,
41 unsigned int size_to_flush
) {
42 result_size_
= result_size
;
43 default_buffer_size_
= default_buffer_size
;
44 min_buffer_size_
= min_buffer_size
;
45 max_buffer_size_
= max_buffer_size
;
46 alignment_
= alignment
;
47 size_to_flush_
= size_to_flush
;
48 ReallocateRingBuffer(default_buffer_size_
- result_size
);
52 void TransferBuffer::Free() {
55 helper_
->command_buffer()->DestroyTransferBuffer(buffer_id_
);
59 result_buffer_
= NULL
;
60 result_shm_offset_
= 0;
62 bytes_since_last_flush_
= 0;
66 bool TransferBuffer::HaveBuffer() const {
67 return buffer_id_
!= -1;
70 RingBuffer::Offset
TransferBuffer::GetOffset(void* pointer
) const {
71 return ring_buffer_
->GetOffset(pointer
);
74 void TransferBuffer::FreePendingToken(void* p
, unsigned int token
) {
75 ring_buffer_
->FreePendingToken(p
, token
);
76 if (bytes_since_last_flush_
>= size_to_flush_
&& size_to_flush_
> 0) {
78 bytes_since_last_flush_
= 0;
82 void TransferBuffer::AllocateRingBuffer(unsigned int size
) {
83 for (;size
>= min_buffer_size_
; size
/= 2) {
86 helper_
->command_buffer()->CreateTransferBuffer(size
, &id
);
89 ring_buffer_
.reset(new AlignedRingBuffer(
93 buffer_
.size
- result_size_
,
95 static_cast<char*>(buffer_
.ptr
) + result_size_
));
97 result_buffer_
= buffer_
.ptr
;
98 result_shm_offset_
= 0;
101 // we failed so don't try larger than this.
102 max_buffer_size_
= size
/ 2;
107 // Returns the integer i such as 2^i <= n < 2^(i+1)
108 static int Log2Floor(uint32 n
) {
113 for (int i
= 4; i
>= 0; --i
) {
114 int shift
= (1 << i
);
115 uint32 x
= value
>> shift
;
121 GPU_DCHECK_EQ(value
, 1u);
125 // Returns the integer i such as 2^(i-1) < n <= 2^i
126 static int Log2Ceiling(uint32 n
) {
130 // Log2Floor returns -1 for 0, so the following works correctly for n=1.
131 return 1 + Log2Floor(n
- 1);
135 static unsigned int ComputePOTSize(unsigned int dimension
) {
136 return (dimension
== 0) ? 0 : 1 << Log2Ceiling(dimension
);
139 void TransferBuffer::ReallocateRingBuffer(unsigned int size
) {
140 // What size buffer would we ask for if we needed a new one?
141 unsigned int needed_buffer_size
= ComputePOTSize(size
+ result_size_
);
142 needed_buffer_size
= std::max(needed_buffer_size
, min_buffer_size_
);
143 needed_buffer_size
= std::max(needed_buffer_size
, default_buffer_size_
);
144 needed_buffer_size
= std::min(needed_buffer_size
, max_buffer_size_
);
146 if (usable_
&& (!HaveBuffer() || needed_buffer_size
> buffer_
.size
)) {
150 AllocateRingBuffer(needed_buffer_size
);
154 void* TransferBuffer::AllocUpTo(
155 unsigned int size
, unsigned int* size_allocated
) {
156 GPU_DCHECK(size_allocated
);
158 ReallocateRingBuffer(size
);
164 unsigned int max_size
= ring_buffer_
->GetLargestFreeOrPendingSize();
165 *size_allocated
= std::min(max_size
, size
);
166 bytes_since_last_flush_
+= *size_allocated
;
167 return ring_buffer_
->Alloc(*size_allocated
);
170 void* TransferBuffer::Alloc(unsigned int size
) {
171 ReallocateRingBuffer(size
);
177 unsigned int max_size
= ring_buffer_
->GetLargestFreeOrPendingSize();
178 if (size
> max_size
) {
182 bytes_since_last_flush_
+= size
;
183 return ring_buffer_
->Alloc(size
);
186 void* TransferBuffer::GetResultBuffer() {
187 ReallocateRingBuffer(result_size_
);
188 return result_buffer_
;
191 int TransferBuffer::GetResultOffset() {
192 ReallocateRingBuffer(result_size_
);
193 return result_shm_offset_
;
196 int TransferBuffer::GetShmId() {
197 ReallocateRingBuffer(result_size_
);
201 unsigned int TransferBuffer::GetCurrentMaxAllocationWithoutRealloc() const {
202 return HaveBuffer() ? ring_buffer_
->GetLargestFreeOrPendingSize() : 0;
205 unsigned int TransferBuffer::GetMaxAllocation() const {
206 return HaveBuffer() ? max_buffer_size_
- result_size_
: 0;
209 void ScopedTransferBufferPtr::Release() {
211 transfer_buffer_
->FreePendingToken(buffer_
, helper_
->InsertToken());
217 void ScopedTransferBufferPtr::Reset(unsigned int new_size
) {
219 // NOTE: we allocate buffers of size 0 so that HaveBuffer will be true, so
220 // that address will return a pointer just like malloc, and so that GetShmId
221 // will be valid. That has the side effect that we'll insert a token on free.
222 // We could add code skip the token for a zero size buffer but it doesn't seem
223 // worth the complication.
224 buffer_
= transfer_buffer_
->AllocUpTo(new_size
, &size_
);