| // Copyright (c) 2012 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. |
| |
| // This file contains the definition of the RingBuffer class. |
| |
| #ifndef GPU_COMMAND_BUFFER_CLIENT_RING_BUFFER_H_ |
| #define GPU_COMMAND_BUFFER_CLIENT_RING_BUFFER_H_ |
| |
| #include <stdint.h> |
| |
| #include "base/containers/circular_deque.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "gpu/gpu_export.h" |
| |
| namespace gpu { |
| class CommandBufferHelper; |
| |
| // RingBuffer manages a piece of memory as a ring buffer. Memory is allocated |
| // with Alloc and then a is freed pending a token with FreePendingToken. Old |
| // allocations must not be kept past new allocations. |
| class GPU_EXPORT RingBuffer { |
| public: |
| typedef uint32_t Offset; |
| |
| // Creates a RingBuffer. |
| // Parameters: |
| // alignment: Alignment for allocations. |
| // base_offset: The offset of the start of the buffer. |
| // size: The size of the buffer in bytes. |
| // helper: A CommandBufferHelper for dealing with tokens. |
| // base: The physical address that corresponds to base_offset. |
| RingBuffer(uint32_t alignment, |
| Offset base_offset, |
| uint32_t size, |
| CommandBufferHelper* helper, |
| void* base); |
| |
| ~RingBuffer(); |
| |
| // Allocates a block of memory. If the buffer is out of directly available |
| // memory, this function may wait until memory that was freed "pending a |
| // token" can be re-used. The safest pattern of allocation is to only have |
| // one used allocation at once. Allocating while NumUsedBlocks > 0 can |
| // lead to deadlock if the entire buffer is exhausted. In this case, it is |
| // recommended to only Alloc smaller than GetFreeSizeNoWaiting. |
| // |
| // Parameters: |
| // size: the size of the memory block to allocate. |
| // |
| // Returns: |
| // the pointer to the allocated memory block. |
| void* Alloc(uint32_t size); |
| |
| // Frees a block of memory, pending the passage of a token. That memory won't |
| // be re-allocated until the token has passed through the command stream. |
| // If a block is freed out of order, that hole will be counted as used |
| // in the Get*FreeSize* functions below. |
| // |
| // Parameters: |
| // pointer: the pointer to the memory block to free. |
| // token: the token value to wait for before re-using the memory. |
| void FreePendingToken(void* pointer, uint32_t token); |
| |
| // Discards a block within the ring buffer. |
| // |
| // Parameters: |
| // pointer: the pointer to the memory block to free. |
| void DiscardBlock(void* pointer); |
| |
| // Gets the size of the largest free block that is available without waiting. |
| uint32_t GetLargestFreeSizeNoWaiting(); |
| |
| // Gets the total size of all free blocks that are available without waiting. |
| uint32_t GetTotalFreeSizeNoWaiting(); |
| |
| // Gets the size of the largest free block that can be allocated if the |
| // caller can wait. Allocating a block of this size will succeed, but may |
| // block. |
| uint32_t GetLargestFreeOrPendingSize() { |
| // If size_ is not a multiple of alignment_, then trying to allocate it will |
| // cause us to try to allocate more than we actually can due to rounding up. |
| // So, round down here. |
| return size_ - size_ % alignment_; |
| } |
| |
| // Total size minus usable size. |
| uint32_t GetUsedSize() { return size_ - GetLargestFreeSizeNoWaiting(); } |
| |
| uint32_t NumUsedBlocks() const { return num_used_blocks_; } |
| |
| // Gets a pointer to a memory block given the base memory and the offset. |
| void* GetPointer(RingBuffer::Offset offset) const { |
| return static_cast<int8_t*>(base_) + offset; |
| } |
| |
| // Gets the offset to a memory block given the base memory and the address. |
| RingBuffer::Offset GetOffset(void* pointer) const { |
| return static_cast<int8_t*>(pointer) - static_cast<int8_t*>(base_); |
| } |
| |
| // Rounds the given size to the alignment in use. |
| uint32_t RoundToAlignment(uint32_t size) { |
| return (size + alignment_ - 1) & ~(alignment_ - 1); |
| } |
| |
| // Shrinks the last block. new_size must be smaller than the current size |
| // and the block must still be in use in order to shrink. |
| void ShrinkLastBlock(uint32_t new_size); |
| |
| private: |
| enum State { |
| IN_USE, |
| PADDING, |
| FREE_PENDING_TOKEN |
| }; |
| // Book-keeping sturcture that describes a block of memory. |
| struct Block { |
| Block(Offset _offset, uint32_t _size, State _state) |
| : offset(_offset), size(_size), token(0), state(_state) {} |
| Offset offset; |
| uint32_t size; |
| uint32_t token; // token to wait for. |
| State state; |
| }; |
| |
| using Container = base::circular_deque<Block>; |
| using BlockIndex = uint32_t; |
| |
| void FreeOldestBlock(); |
| uint32_t GetLargestFreeSizeNoWaitingInternal(); |
| |
| CommandBufferHelper* helper_; |
| |
| // Used blocks are added to the end, blocks are freed from the beginning. |
| Container blocks_; |
| |
| // The base offset of the ring buffer. |
| Offset base_offset_; |
| |
| // The size of the ring buffer. |
| Offset size_; |
| |
| // Offset of first free byte. |
| Offset free_offset_ = 0; |
| |
| // Offset of first used byte. |
| // Range between in_use_mark and free_mark is in use. |
| Offset in_use_offset_ = 0; |
| |
| // Alignment for allocations. |
| uint32_t alignment_; |
| |
| // Number of blocks in |blocks_| that are in the IN_USE state. |
| uint32_t num_used_blocks_ = 0; |
| |
| // The physical address that corresponds to base_offset. |
| void* base_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(RingBuffer); |
| }; |
| |
| } // namespace gpu |
| |
| #endif // GPU_COMMAND_BUFFER_CLIENT_RING_BUFFER_H_ |