Index: gpu/command_buffer/client/client_discardable_manager.cc |
diff --git a/gpu/command_buffer/client/client_discardable_manager.cc b/gpu/command_buffer/client/client_discardable_manager.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..5b2dbb6ffb2e9e03279b5969ecbf8c04952764b7 |
--- /dev/null |
+++ b/gpu/command_buffer/client/client_discardable_manager.cc |
@@ -0,0 +1,224 @@ |
+// Copyright (c) 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "gpu/command_buffer/client/client_discardable_manager.h" |
+ |
+#include "base/memory/ptr_util.h" |
+#include "base/sys_info.h" |
+ |
+namespace gpu { |
+namespace { |
+ |
+// Stores a set of offsets, initially 0 to |element_count_|. Allows callers to |
+// take and return offsets from the set. Internally stores the offsets as a set |
+// of ranges. This means that in the worst case (every other offset taken), the |
+// set will use |element_count_| uints, but should typically use fewer. |
+class FreeOffsetSet { |
+ public: |
+ // Creates a new set, containing 0 to |element_count|. |
+ FreeOffsetSet(uint32_t element_count); |
piman
2017/05/08 20:20:27
nit: explicit
ericrk
2017/05/10 21:36:02
Done.
|
+ |
+ // Returns true if the set contains at least one element. |
+ bool HasFreeOffset() const; |
+ |
+ // Returns true if any element from the set has been taken. |
+ bool HasUsedOffset() const; |
+ |
+ // Takes a free offset from the set. Should only be called if HasFreeOffset(). |
+ uint32_t TakeFreeOffset(); |
+ |
+ // Returns an offset to the set. |
+ void ReturnFreeOffset(uint32_t offset); |
+ |
+ private: |
+ struct FreeRange { |
+ uint32_t start; |
+ uint32_t end; |
+ }; |
+ struct CompareFreeRanges { |
+ bool operator()(const FreeRange& a, const FreeRange& b) const { |
+ return a.start < b.start; |
+ } |
+ }; |
+ |
+ const uint32_t element_count_; |
+ 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.
|
+ |
+ DISALLOW_COPY_AND_ASSIGN(FreeOffsetSet); |
+}; |
+ |
+FreeOffsetSet::FreeOffsetSet(uint32_t element_count) |
+ : element_count_(element_count) { |
+ free_ranges_.insert({0, element_count_}); |
+} |
+ |
+bool FreeOffsetSet::HasFreeOffset() const { |
+ return !free_ranges_.empty(); |
+} |
+ |
+bool FreeOffsetSet::HasUsedOffset() const { |
+ if (free_ranges_.size() != 1 || free_ranges_.begin()->start != 0 || |
+ free_ranges_.begin()->end != element_count_) |
+ return true; |
+ |
+ return false; |
+} |
+ |
+uint32_t FreeOffsetSet::TakeFreeOffset() { |
+ DCHECK(HasFreeOffset()); |
+ |
+ auto it = free_ranges_.begin(); |
+ uint32_t offset_to_return = it->start; |
+ |
+ FreeRange new_range{it->start + 1, it->end}; |
+ free_ranges_.erase(it); |
+ if (new_range.start != new_range.end) |
+ free_ranges_.insert(new_range); |
+ |
+ return offset_to_return; |
+} |
+ |
+void FreeOffsetSet::ReturnFreeOffset(uint32_t offset) { |
+ FreeRange new_range{offset, offset + 1}; |
+ |
+ // Find the FreeRange directly before/after our new range. |
+ auto next_range = free_ranges_.lower_bound(new_range); |
+ auto prev_range = free_ranges_.end(); |
+ if (next_range != free_ranges_.begin()) { |
+ prev_range = std::prev(next_range); |
+ } |
+ |
+ // Collapse ranges if possible. |
+ if (next_range != free_ranges_.end() && next_range->start == new_range.end) { |
+ new_range.end = next_range->end; |
+ free_ranges_.erase(next_range); |
+ } |
+ |
+ if (prev_range != free_ranges_.end() && prev_range->end == new_range.start) { |
+ new_range.start = prev_range->start; |
+ free_ranges_.erase(prev_range); |
+ } |
+ |
+ free_ranges_.insert(new_range); |
+} |
+ |
+// Returns the size of the allocation which ClientDiscardableManager will |
+// sub-allocate from. This should be at least as big as the minimum shared |
+// memory allocation size. |
+size_t AllocationSize() { |
+ size_t allocation_size = base::SysInfo::VMAllocationGranularity(); |
+ // If the allocation is small (less than 2K), round it up to at least 4K. |
+ allocation_size = std::max(static_cast<size_t>(2048), allocation_size); |
+ return allocation_size; |
+} |
+ |
+} // namespace |
+ |
+struct ClientDiscardableManager::Allocation { |
+ Allocation(uint32_t element_count) : free_offsets(element_count) {} |
+ |
+ scoped_refptr<Buffer> buffer; |
+ int32_t shm_id = 0; |
+ FreeOffsetSet free_offsets; |
+}; |
+ |
+ClientDiscardableManager::ClientDiscardableManager() |
+ : allocation_size_(AllocationSize()) {} |
+ClientDiscardableManager::~ClientDiscardableManager() = default; |
+ |
+ClientDiscardableHandle ClientDiscardableManager::InitializeTexture( |
+ CommandBuffer* command_buffer, |
+ uint32_t texture_id) { |
+ DCHECK(texture_handles_.find(texture_id) == texture_handles_.end()); |
+ |
+ scoped_refptr<Buffer> buffer; |
+ uint32_t offset = 0; |
+ int32_t shm_id = 0; |
+ FindAllocation(command_buffer, &buffer, &shm_id, &offset); |
+ uint32_t byte_offset = offset * element_size_; |
+ ClientDiscardableHandle handle(std::move(buffer), byte_offset, shm_id); |
+ texture_handles_.emplace(texture_id, handle); |
+ return handle; |
+} |
+ |
+bool ClientDiscardableManager::LockTexture(uint32_t texture_id) { |
+ auto found = texture_handles_.find(texture_id); |
+ DCHECK(found != texture_handles_.end()); |
+ return found->second.Lock(); |
+} |
+ |
+void ClientDiscardableManager::FreeTexture(uint32_t texture_id) { |
+ auto found = texture_handles_.find(texture_id); |
+ if (found == texture_handles_.end()) |
+ return; |
+ pending_handles_.push(found->second); |
+ texture_handles_.erase(found); |
+} |
+ |
+bool ClientDiscardableManager::TextureIsValid(uint32_t texture_id) const { |
+ return texture_handles_.find(texture_id) != texture_handles_.end(); |
+} |
+ |
+void ClientDiscardableManager::FindAllocation(CommandBuffer* command_buffer, |
+ scoped_refptr<Buffer>* buffer, |
+ int32_t* shm_id, |
+ uint32_t* offset) { |
+ 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
|
+ |
+ for (auto& allocation : allocations_) { |
+ if (!allocation->free_offsets.HasFreeOffset()) |
+ continue; |
+ |
+ *offset = allocation->free_offsets.TakeFreeOffset(); |
+ *shm_id = allocation->shm_id; |
+ *buffer = allocation->buffer; |
+ return; |
+ } |
+ |
+ // We couldn't find an existing free entry. Allocate more space. |
+ auto allocation = base::MakeUnique<Allocation>(elements_per_allocation_); |
+ allocation->buffer = command_buffer->CreateTransferBuffer( |
+ allocation_size_, &allocation->shm_id); |
+ |
+ *offset = allocation->free_offsets.TakeFreeOffset(); |
+ *shm_id = allocation->shm_id; |
+ *buffer = allocation->buffer; |
+ allocations_.push_back(std::move(allocation)); |
+} |
+ |
+void ClientDiscardableManager::ReturnAllocation( |
+ CommandBuffer* command_buffer, |
+ const ClientDiscardableHandle& handle) { |
+ for (auto it = allocations_.begin(); it != allocations_.end(); ++it) { |
+ Allocation* allocation = it->get(); |
+ if (allocation->shm_id != handle.shm_id()) |
+ continue; |
+ |
+ allocation->free_offsets.ReturnFreeOffset(handle.byte_offset() / |
+ element_size_); |
+ |
+ if (!allocation->free_offsets.HasUsedOffset()) { |
+ command_buffer->DestroyTransferBuffer(allocation->shm_id); |
+ allocations_.erase(it); |
+ return; |
+ } |
+ } |
+} |
+ |
+void ClientDiscardableManager::CheckPending(CommandBuffer* command_buffer) { |
+ while (pending_handles_.size() > 0 && |
+ pending_handles_.front().CanBeReUsed()) { |
+ ReturnAllocation(command_buffer, pending_handles_.front()); |
+ pending_handles_.pop(); |
+ } |
+} |
+ |
+ClientDiscardableHandle ClientDiscardableManager::GetHandleForTesting( |
+ uint32_t texture_id) { |
+ auto found = texture_handles_.find(texture_id); |
+ DCHECK(found != texture_handles_.end()); |
+ return found->second; |
+} |
+ |
+} // namespace gpu |