| // Copyright 2017 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/renderer/rasterizer/egl/rect_allocator.h" |
| |
| #include <algorithm> |
| |
| namespace cobalt { |
| namespace renderer { |
| namespace rasterizer { |
| namespace egl { |
| |
| namespace { |
| // Sort by descending area then width and height. This will place smaller |
| // blocks at the end of the free list. Allocations will use the block with |
| // the smallest area of sufficient dimensions in order to minimize waste. |
| bool FirstRectIsBigger(const math::Rect& a, const math::Rect& b) { |
| const float area_a = a.width() * a.height(); |
| const float area_b = b.width() * b.height(); |
| return (area_a > area_b) || |
| (area_a == area_b && (a.width() > b.width() || |
| a.height() > b.height())); |
| } |
| } // namespace |
| |
| RectAllocator::RectAllocator() { |
| Reset(math::Size(0, 0)); |
| } |
| |
| RectAllocator::RectAllocator(const math::Size& total_size) { |
| Reset(total_size); |
| } |
| |
| void RectAllocator::Reset(const math::Size& total_size) { |
| total_size_ = total_size; |
| |
| unused_.clear(); |
| unused_.push_back( |
| math::Rect(0, 0, total_size_.width(), total_size_.height())); |
| } |
| |
| math::Rect RectAllocator::Allocate(const math::Size& alloc_size) { |
| math::Rect allocation(0, 0, alloc_size.width(), alloc_size.height()); |
| |
| // Find the first block that is too small for the requested allocation. |
| MemoryList::iterator pos = std::upper_bound( |
| unused_.begin(), unused_.end(), allocation, FirstRectIsBigger); |
| |
| // All blocks before "pos" will have sufficient area, but a sequential search |
| // must be used to find a block of sufficient width and height. |
| while (pos != unused_.begin()) { |
| --pos; |
| if (pos->width() >= allocation.width() && |
| pos->height() >= allocation.height()) { |
| // Found an unused block big enough for the allocation. |
| math::Rect memory = *pos; |
| unused_.erase(pos); |
| |
| // Use top-left of memory block for the allocation, and return the |
| // bottom and right portions as unused blocks. The split can be done |
| // in one of two ways: |
| // +-------+-------+ +-------+-------+ |
| // | Alloc | Right | | Alloc | | |
| // +-------+-------+ or +-------+ Right + |
| // | Bottom | |Bottom | | |
| // +---------------+ +-------+-------+ |
| allocation.set_x(memory.x()); |
| allocation.set_y(memory.y()); |
| |
| if (memory.width() != allocation.width()) { |
| if (memory.height() != allocation.height()) { |
| // Return bottom and right of memory block as unused. |
| // Preserve the largest block. |
| const float remaining_width = memory.width() - allocation.width(); |
| const float remaining_height = memory.height() - allocation.height(); |
| if (memory.width() * remaining_height >= |
| remaining_width * memory.height()) { |
| // Bottom portion is bigger. |
| math::Rect right(memory.x() + allocation.width(), memory.y(), |
| remaining_width, allocation.height()); |
| memory.set_y(memory.y() + allocation.height()); |
| memory.set_height(remaining_height); |
| AddUnused(memory); |
| AddUnused(right); |
| } else { |
| // Right portion is bigger. |
| math::Rect bottom(memory.x(), memory.y() + allocation.height(), |
| allocation.width(), remaining_height); |
| memory.set_x(memory.x() + allocation.width()); |
| memory.set_width(remaining_width); |
| AddUnused(memory); |
| AddUnused(bottom); |
| } |
| } else { |
| // Return right of memory block as unused. |
| memory.set_x(memory.x() + allocation.width()); |
| memory.set_width(memory.width() - allocation.width()); |
| AddUnused(memory); |
| } |
| } else if (memory.height() != allocation.height()) { |
| // Return bottom of memory block as unused. |
| memory.set_y(memory.y() + allocation.height()); |
| memory.set_height(memory.height() - allocation.height()); |
| AddUnused(memory); |
| } |
| |
| return allocation; |
| } |
| } |
| |
| // Not enough unused space for the allocation. |
| return math::Rect(0, 0, 0, 0); |
| } |
| |
| void RectAllocator::AddUnused(const math::Rect& memory) { |
| DCHECK_LE(memory.right(), total_size_.width()); |
| DCHECK_LE(memory.bottom(), total_size_.height()); |
| MemoryList::iterator pos = std::lower_bound( |
| unused_.begin(), unused_.end(), memory, FirstRectIsBigger); |
| unused_.insert(pos, memory); |
| } |
| |
| } // namespace egl |
| } // namespace rasterizer |
| } // namespace renderer |
| } // namespace cobalt |