OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2017 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 #include "gpu/command_buffer/client/client_discardable_manager.h" | |
6 | |
7 #include "base/memory/ptr_util.h" | |
8 #include "base/sys_info.h" | |
9 | |
10 namespace gpu { | |
11 namespace { | |
12 | |
13 // Stores a set of offsets, initially 0 to |element_count_|. Allows callers to | |
14 // take and return offsets from the set. Internally stores the offsets as a set | |
15 // of ranges. This means that in the worst case (every other offset taken), the | |
16 // set will use |element_count_| uints, but should typically use fewer. | |
17 class FreeOffsetSet { | |
18 public: | |
19 // Creates a new set, containing 0 to |element_count|. | |
20 FreeOffsetSet(uint32_t element_count); | |
piman
2017/05/08 20:20:27
nit: explicit
ericrk
2017/05/10 21:36:02
Done.
| |
21 | |
22 // Returns true if the set contains at least one element. | |
23 bool HasFreeOffset() const; | |
24 | |
25 // Returns true if any element from the set has been taken. | |
26 bool HasUsedOffset() const; | |
27 | |
28 // Takes a free offset from the set. Should only be called if HasFreeOffset(). | |
29 uint32_t TakeFreeOffset(); | |
30 | |
31 // Returns an offset to the set. | |
32 void ReturnFreeOffset(uint32_t offset); | |
33 | |
34 private: | |
35 struct FreeRange { | |
36 uint32_t start; | |
37 uint32_t end; | |
38 }; | |
39 struct CompareFreeRanges { | |
40 bool operator()(const FreeRange& a, const FreeRange& b) const { | |
41 return a.start < b.start; | |
42 } | |
43 }; | |
44 | |
45 const uint32_t element_count_; | |
46 std::set<FreeRange, CompareFreeRanges> free_ranges_; | |
piman
2017/05/08 20:20:27
nit: base::flat_set? For 4k pages, we'll always ha
ericrk
2017/05/10 21:36:02
Makes sense - will give this a try.
| |
47 | |
48 DISALLOW_COPY_AND_ASSIGN(FreeOffsetSet); | |
49 }; | |
50 | |
51 FreeOffsetSet::FreeOffsetSet(uint32_t element_count) | |
52 : element_count_(element_count) { | |
53 free_ranges_.insert({0, element_count_}); | |
54 } | |
55 | |
56 bool FreeOffsetSet::HasFreeOffset() const { | |
57 return !free_ranges_.empty(); | |
58 } | |
59 | |
60 bool FreeOffsetSet::HasUsedOffset() const { | |
61 if (free_ranges_.size() != 1 || free_ranges_.begin()->start != 0 || | |
62 free_ranges_.begin()->end != element_count_) | |
63 return true; | |
64 | |
65 return false; | |
66 } | |
67 | |
68 uint32_t FreeOffsetSet::TakeFreeOffset() { | |
69 DCHECK(HasFreeOffset()); | |
70 | |
71 auto it = free_ranges_.begin(); | |
72 uint32_t offset_to_return = it->start; | |
73 | |
74 FreeRange new_range{it->start + 1, it->end}; | |
75 free_ranges_.erase(it); | |
76 if (new_range.start != new_range.end) | |
77 free_ranges_.insert(new_range); | |
78 | |
79 return offset_to_return; | |
80 } | |
81 | |
82 void FreeOffsetSet::ReturnFreeOffset(uint32_t offset) { | |
83 FreeRange new_range{offset, offset + 1}; | |
84 | |
85 // Find the FreeRange directly before/after our new range. | |
86 auto next_range = free_ranges_.lower_bound(new_range); | |
87 auto prev_range = free_ranges_.end(); | |
88 if (next_range != free_ranges_.begin()) { | |
89 prev_range = std::prev(next_range); | |
90 } | |
91 | |
92 // Collapse ranges if possible. | |
93 if (next_range != free_ranges_.end() && next_range->start == new_range.end) { | |
94 new_range.end = next_range->end; | |
95 free_ranges_.erase(next_range); | |
96 } | |
97 | |
98 if (prev_range != free_ranges_.end() && prev_range->end == new_range.start) { | |
99 new_range.start = prev_range->start; | |
100 free_ranges_.erase(prev_range); | |
101 } | |
102 | |
103 free_ranges_.insert(new_range); | |
104 } | |
105 | |
106 // Returns the size of the allocation which ClientDiscardableManager will | |
107 // sub-allocate from. This should be at least as big as the minimum shared | |
108 // memory allocation size. | |
109 size_t AllocationSize() { | |
110 size_t allocation_size = base::SysInfo::VMAllocationGranularity(); | |
111 // If the allocation is small (less than 2K), round it up to at least 4K. | |
112 allocation_size = std::max(static_cast<size_t>(2048), allocation_size); | |
113 return allocation_size; | |
114 } | |
115 | |
116 } // namespace | |
117 | |
118 struct ClientDiscardableManager::Allocation { | |
119 Allocation(uint32_t element_count) : free_offsets(element_count) {} | |
120 | |
121 scoped_refptr<Buffer> buffer; | |
122 int32_t shm_id = 0; | |
123 FreeOffsetSet free_offsets; | |
124 }; | |
125 | |
126 ClientDiscardableManager::ClientDiscardableManager() | |
127 : allocation_size_(AllocationSize()) {} | |
128 ClientDiscardableManager::~ClientDiscardableManager() = default; | |
129 | |
130 ClientDiscardableHandle ClientDiscardableManager::InitializeTexture( | |
131 CommandBuffer* command_buffer, | |
132 uint32_t texture_id) { | |
133 DCHECK(texture_handles_.find(texture_id) == texture_handles_.end()); | |
134 | |
135 scoped_refptr<Buffer> buffer; | |
136 uint32_t offset = 0; | |
137 int32_t shm_id = 0; | |
138 FindAllocation(command_buffer, &buffer, &shm_id, &offset); | |
139 uint32_t byte_offset = offset * element_size_; | |
140 ClientDiscardableHandle handle(std::move(buffer), byte_offset, shm_id); | |
141 texture_handles_.emplace(texture_id, handle); | |
142 return handle; | |
143 } | |
144 | |
145 bool ClientDiscardableManager::LockTexture(uint32_t texture_id) { | |
146 auto found = texture_handles_.find(texture_id); | |
147 DCHECK(found != texture_handles_.end()); | |
148 return found->second.Lock(); | |
149 } | |
150 | |
151 void ClientDiscardableManager::FreeTexture(uint32_t texture_id) { | |
152 auto found = texture_handles_.find(texture_id); | |
153 if (found == texture_handles_.end()) | |
154 return; | |
155 pending_handles_.push(found->second); | |
156 texture_handles_.erase(found); | |
157 } | |
158 | |
159 bool ClientDiscardableManager::TextureIsValid(uint32_t texture_id) const { | |
160 return texture_handles_.find(texture_id) != texture_handles_.end(); | |
161 } | |
162 | |
163 void ClientDiscardableManager::FindAllocation(CommandBuffer* command_buffer, | |
164 scoped_refptr<Buffer>* buffer, | |
165 int32_t* shm_id, | |
166 uint32_t* offset) { | |
167 CheckPending(command_buffer); | |
piman
2017/05/02 22:21:03
The logic for this seems very similar to MappedMem
ericrk
2017/05/08 01:03:50
Good point. One thing which is a bit tricky is ens
piman
2017/05/08 20:20:27
Ok, fair enough. We have a bit of logic in e.g. St
| |
168 | |
169 for (auto& allocation : allocations_) { | |
170 if (!allocation->free_offsets.HasFreeOffset()) | |
171 continue; | |
172 | |
173 *offset = allocation->free_offsets.TakeFreeOffset(); | |
174 *shm_id = allocation->shm_id; | |
175 *buffer = allocation->buffer; | |
176 return; | |
177 } | |
178 | |
179 // We couldn't find an existing free entry. Allocate more space. | |
180 auto allocation = base::MakeUnique<Allocation>(elements_per_allocation_); | |
181 allocation->buffer = command_buffer->CreateTransferBuffer( | |
182 allocation_size_, &allocation->shm_id); | |
183 | |
184 *offset = allocation->free_offsets.TakeFreeOffset(); | |
185 *shm_id = allocation->shm_id; | |
186 *buffer = allocation->buffer; | |
187 allocations_.push_back(std::move(allocation)); | |
188 } | |
189 | |
190 void ClientDiscardableManager::ReturnAllocation( | |
191 CommandBuffer* command_buffer, | |
192 const ClientDiscardableHandle& handle) { | |
193 for (auto it = allocations_.begin(); it != allocations_.end(); ++it) { | |
194 Allocation* allocation = it->get(); | |
195 if (allocation->shm_id != handle.shm_id()) | |
196 continue; | |
197 | |
198 allocation->free_offsets.ReturnFreeOffset(handle.byte_offset() / | |
199 element_size_); | |
200 | |
201 if (!allocation->free_offsets.HasUsedOffset()) { | |
202 command_buffer->DestroyTransferBuffer(allocation->shm_id); | |
203 allocations_.erase(it); | |
204 return; | |
205 } | |
206 } | |
207 } | |
208 | |
209 void ClientDiscardableManager::CheckPending(CommandBuffer* command_buffer) { | |
210 while (pending_handles_.size() > 0 && | |
211 pending_handles_.front().CanBeReUsed()) { | |
212 ReturnAllocation(command_buffer, pending_handles_.front()); | |
213 pending_handles_.pop(); | |
214 } | |
215 } | |
216 | |
217 ClientDiscardableHandle ClientDiscardableManager::GetHandleForTesting( | |
218 uint32_t texture_id) { | |
219 auto found = texture_handles_.find(texture_id); | |
220 DCHECK(found != texture_handles_.end()); | |
221 return found->second; | |
222 } | |
223 | |
224 } // namespace gpu | |
OLD | NEW |