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"
10 #include "base/logging.h"
11 #include "base/trace_event/trace_event.h"
12 #include "gpu/command_buffer/client/cmd_buffer_helper.h"
16 TransferBuffer::TransferBuffer(
17 CommandBufferHelper
* helper
)
20 default_buffer_size_(0),
25 bytes_since_last_flush_(0),
28 result_shm_offset_(0),
32 TransferBuffer::~TransferBuffer() {
36 bool TransferBuffer::Initialize(
37 unsigned int default_buffer_size
,
38 unsigned int result_size
,
39 unsigned int min_buffer_size
,
40 unsigned int max_buffer_size
,
41 unsigned int alignment
,
42 unsigned int size_to_flush
) {
43 result_size_
= result_size
;
44 default_buffer_size_
= default_buffer_size
;
45 min_buffer_size_
= min_buffer_size
;
46 max_buffer_size_
= max_buffer_size
;
47 alignment_
= alignment
;
48 size_to_flush_
= size_to_flush
;
49 ReallocateRingBuffer(default_buffer_size_
- result_size
);
53 void TransferBuffer::Free() {
55 TRACE_EVENT0("gpu", "TransferBuffer::Free");
57 helper_
->command_buffer()->DestroyTransferBuffer(buffer_id_
);
60 result_buffer_
= NULL
;
61 result_shm_offset_
= 0;
63 bytes_since_last_flush_
= 0;
67 bool TransferBuffer::HaveBuffer() const {
68 DCHECK(buffer_id_
== -1 || buffer_
.get());
69 return buffer_id_
!= -1;
72 RingBuffer::Offset
TransferBuffer::GetOffset(void* pointer
) const {
73 return ring_buffer_
->GetOffset(pointer
);
76 void TransferBuffer::DiscardBlock(void* p
) {
77 ring_buffer_
->DiscardBlock(p
);
80 void TransferBuffer::FreePendingToken(void* p
, unsigned int token
) {
81 ring_buffer_
->FreePendingToken(p
, token
);
82 if (bytes_since_last_flush_
>= size_to_flush_
&& size_to_flush_
> 0) {
84 bytes_since_last_flush_
= 0;
88 unsigned int TransferBuffer::GetSize() const {
89 return HaveBuffer() ? ring_buffer_
->GetLargestFreeOrPendingSize() : 0;
92 unsigned int TransferBuffer::GetFreeSize() const {
93 return HaveBuffer() ? ring_buffer_
->GetTotalFreeSizeNoWaiting() : 0;
96 void TransferBuffer::AllocateRingBuffer(unsigned int size
) {
97 for (;size
>= min_buffer_size_
; size
/= 2) {
99 scoped_refptr
<gpu::Buffer
> buffer
=
100 helper_
->command_buffer()->CreateTransferBuffer(size
, &id
);
102 DCHECK(buffer
.get());
104 ring_buffer_
.reset(new RingBuffer(
107 buffer_
->size() - result_size_
,
109 static_cast<char*>(buffer_
->memory()) + result_size_
));
111 result_buffer_
= buffer_
->memory();
112 result_shm_offset_
= 0;
115 // we failed so don't try larger than this.
116 max_buffer_size_
= size
/ 2;
121 static unsigned int ComputePOTSize(unsigned int dimension
) {
122 return (dimension
== 0) ? 0 : 1 << base::bits::Log2Ceiling(dimension
);
125 void TransferBuffer::ReallocateRingBuffer(unsigned int size
) {
126 // What size buffer would we ask for if we needed a new one?
127 unsigned int needed_buffer_size
= ComputePOTSize(size
+ result_size_
);
128 needed_buffer_size
= std::max(needed_buffer_size
, min_buffer_size_
);
129 needed_buffer_size
= std::max(needed_buffer_size
, default_buffer_size_
);
130 needed_buffer_size
= std::min(needed_buffer_size
, max_buffer_size_
);
132 if (usable_
&& (!HaveBuffer() || needed_buffer_size
> buffer_
->size())) {
136 AllocateRingBuffer(needed_buffer_size
);
140 void* TransferBuffer::AllocUpTo(
141 unsigned int size
, unsigned int* size_allocated
) {
142 DCHECK(size_allocated
);
144 ReallocateRingBuffer(size
);
150 unsigned int max_size
= ring_buffer_
->GetLargestFreeOrPendingSize();
151 *size_allocated
= std::min(max_size
, size
);
152 bytes_since_last_flush_
+= *size_allocated
;
153 return ring_buffer_
->Alloc(*size_allocated
);
156 void* TransferBuffer::Alloc(unsigned int size
) {
157 ReallocateRingBuffer(size
);
163 unsigned int max_size
= ring_buffer_
->GetLargestFreeOrPendingSize();
164 if (size
> max_size
) {
168 bytes_since_last_flush_
+= size
;
169 return ring_buffer_
->Alloc(size
);
172 void* TransferBuffer::GetResultBuffer() {
173 ReallocateRingBuffer(result_size_
);
174 return result_buffer_
;
177 int TransferBuffer::GetResultOffset() {
178 ReallocateRingBuffer(result_size_
);
179 return result_shm_offset_
;
182 int TransferBuffer::GetShmId() {
183 ReallocateRingBuffer(result_size_
);
187 unsigned int TransferBuffer::GetCurrentMaxAllocationWithoutRealloc() const {
188 return HaveBuffer() ? ring_buffer_
->GetLargestFreeOrPendingSize() : 0;
191 unsigned int TransferBuffer::GetMaxAllocation() const {
192 return HaveBuffer() ? max_buffer_size_
- result_size_
: 0;
195 void ScopedTransferBufferPtr::Release() {
197 transfer_buffer_
->FreePendingToken(buffer_
, helper_
->InsertToken());
203 void ScopedTransferBufferPtr::Discard() {
205 transfer_buffer_
->DiscardBlock(buffer_
);
211 void ScopedTransferBufferPtr::Reset(unsigned int new_size
) {
213 // NOTE: we allocate buffers of size 0 so that HaveBuffer will be true, so
214 // that address will return a pointer just like malloc, and so that GetShmId
215 // will be valid. That has the side effect that we'll insert a token on free.
216 // We could add code skip the token for a zero size buffer but it doesn't seem
217 // worth the complication.
218 buffer_
= transfer_buffer_
->AllocUpTo(new_size
, &size_
);