Chromium Code Reviews| 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 |