| // 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 unsigned int 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(unsigned int alignment, | |
| Offset base_offset, | |
| size_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(unsigned int 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, unsigned int 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. | |
| unsigned int GetLargestFreeSizeNoWaiting(); | |
| // Gets the total size of all free blocks that are available without waiting. | |
| unsigned int 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. | |
| unsigned int 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. | |
| unsigned int GetUsedSize() { return size_ - GetLargestFreeSizeNoWaiting(); } | |
| unsigned int 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. | |
| unsigned int RoundToAlignment(unsigned int 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(unsigned int 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, unsigned int _size, State _state) | |
| : offset(_offset), | |
| size(_size), | |
| token(0), | |
| state(_state) { | |
| } | |
| Offset offset; | |
| unsigned int size; | |
| unsigned int token; // token to wait for. | |
| State state; | |
| }; | |
| using Container = base::circular_deque<Block>; | |
| using BlockIndex = unsigned int; | |
| void FreeOldestBlock(); | |
| unsigned int 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. | |
| unsigned int alignment_; | |
| // Number of blocks in |blocks_| that are in the IN_USE state. | |
| unsigned int 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_ |