/* * Copyright 2012 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef GrMemoryPool_DEFINED #define GrMemoryPool_DEFINED #include "include/private/base/SkDebug.h" #include "src/base/SkBlockAllocator.h" #include #include #include #include #ifdef SK_DEBUG #include "src/core/SkTHash.h" #endif /** * Allocates memory in blocks and parcels out space in the blocks for allocation requests. It is * optimized for allocate / release speed over memory efficiency. The interface is designed to be * used to implement operator new and delete overrides. All allocations are expected to be released * before the pool's destructor is called. Allocations will be aligned to sizeof(std::max_align_t). * * All allocated objects must be released back to the memory pool before it can be destroyed. */ class GrMemoryPool { public: #ifdef SK_FORCE_8_BYTE_ALIGNMENT // https://github.com/emscripten-core/emscripten/issues/10072 // Since Skia does not use "long double" (16 bytes), we should be ok to force it back to 8 bytes // until emscripten is fixed. static constexpr size_t kAlignment = 8; #else // Guaranteed alignment of pointer returned by allocate(). static constexpr size_t kAlignment = alignof(std::max_align_t); #endif // Smallest block size allocated on the heap (not the smallest reservation via allocate()). inline static constexpr size_t kMinAllocationSize = 1 << 10; /** * Prealloc size is the amount of space to allocate at pool creation * time and keep around until pool destruction. The min alloc size is * the smallest allowed size of additional allocations. Both sizes are * adjusted to ensure that they are at least as large as kMinAllocationSize * and less than SkBlockAllocator::kMaxAllocationSize. * * Both sizes are what the pool will end up allocating from the system, and * portions of the allocated memory is used for internal bookkeeping. */ static std::unique_ptr Make(size_t preallocSize, size_t minAllocSize); ~GrMemoryPool(); void operator delete(void* p) { ::operator delete(p); } /** * Allocates memory. The memory must be freed with release() before the GrMemoryPool is deleted. */ void* allocate(size_t size); /** * p must have been returned by allocate(). */ void release(void* p); /** * Returns true if there are no unreleased allocations. */ bool isEmpty() const { // If size is the same as preallocSize, there aren't any heap blocks, so currentBlock() // is the inline head block. return fAllocator.currentBlock() == fAllocator.headBlock() && fAllocator.currentBlock()->metadata() == 0; } /** * In debug mode, this reports the IDs of unfreed nodes via `SkDebugf`. This reporting is also * performed automatically whenever a GrMemoryPool is destroyed. * In release mode, this method is a no-op. */ void reportLeaks() const; /** * Returns the total allocated size of the GrMemoryPool minus any preallocated amount */ size_t size() const { return fAllocator.totalSize() - fAllocator.preallocSize(); } /** * Returns the preallocated size of the GrMemoryPool */ size_t preallocSize() const { // Account for the debug-only fields in this count, the offset is 0 for release builds static_assert(std::is_standard_layout::value, ""); return offsetof(GrMemoryPool, fAllocator) + fAllocator.preallocSize(); } /** * Frees any scratch blocks that are no longer being used. */ void resetScratchSpace() { fAllocator.resetScratchSpace(); } #ifdef SK_DEBUG void validate() const; #endif private: // Per-allocation overhead so that GrMemoryPool can always identify the block owning each and // release all occupied bytes, including any resulting from alignment padding. struct Header { int fStart; int fEnd; #if defined(SK_DEBUG) int fID; // ID that can be used to track down leaks by clients. #endif #if defined(SK_DEBUG) || defined(SK_SANITIZE_ADDRESS) uint32_t fSentinel; // set to a known value to check for memory stomping; poisoned in ASAN mode #endif }; GrMemoryPool(size_t preallocSize, size_t minAllocSize); #ifdef SK_DEBUG // Because this exists preallocSize wants to use offsetof, so keep GrMemoryPool standard layout // without depending on THashSet being standard layout. Note that std::unique_ptr may not be // standard layout. struct Debug{ skia_private::THashSet fAllocatedIDs; int fAllocationCount; }; Debug* fDebug{nullptr}; #endif SkBlockAllocator fAllocator; // Must be the last field, in order to use extra allocated space }; #endif