TinyGPU
Loading...
Searching...
No Matches
PSRAMAllocator.h
Go to the documentation of this file.
1#pragma once
2
3/**
4 * @file PSRAMAllocator.h
5 * @brief Header-only C++ allocator that prefers PSRAM allocation on ESP32
6 */
7#ifdef ESP32
8
9#include <cstddef>
10#include <cstdlib>
11#include <cstring>
12#include <new>
13#include <type_traits>
14#include <vector>
15
16
17#include "esp_heap_caps.h"
18
19namespace tinygpu {
20
21/**
22 * @class PSRAMAllocator
23 * @brief STL-compatible allocator that prefers PSRAM allocation with fallback
24 *
25 * PSRAMAllocator is a custom C++ allocator that attempts to allocate memory in
26 * external PSRAM (using heap_caps_aligned_calloc with MALLOC_CAP_SPIRAM) and
27 * falls back to regular heap allocation if PSRAM allocation fails.
28 *
29 * This allocator is particularly useful for ESP32 platforms with external PSRAM
30 * where large buffers (like video frames) should be stored in PSRAM to preserve
31 * internal SRAM for other uses. For small, frequently-accessed buffers that
32 * need fast access, consider using RAMAllocator instead.
33 *
34 * @tparam T The type of objects to allocate
35 *
36 * @note On ESP-IDF platforms, uses heap_caps_aligned_calloc for PSRAM
37 * allocation
38 * @note On Arduino platforms, the heap_caps functions are shimmed to standard
39 * malloc/free
40 * @note Memory is zero-initialized (calloc behavior)
41 * @note Provides proper alignment for the allocated type
42 *
43 * Example usage:
44 * @code
45 * #include "PSRAMAllocator.h"
46 *
47 * // Use PSRAMAllocator with Vector for large buffers
48 * std::vector<uint8_t, PSRAMAllocator<uint8_t>> largeBuffer;
49 * largeBuffer.resize(1024 * 1024); // Large buffer preferably in PSRAM
50 *
51 * @endcode
52 */
53template <typename T = uint8_t>
54class PSRAMAllocator {
55 public:
56 /** @brief The type of objects this allocator can allocate */
57 using value_type = T;
58
59 /**
60 * @brief Default constructor
61 *
62 * Constructs a PSRAMAllocator. No initialization needed.
63 */
64 PSRAMAllocator() noexcept {}
65
66 /**
67 * @brief Copy constructor from different allocator type
68 *
69 * Allows construction from PSRAMAllocator of different type U.
70 * Required for STL allocator requirements.
71 *
72 * @tparam U The type parameter of the source allocator
73 * @param other Source allocator (unused)
74 */
75 template <class U>
76 PSRAMAllocator(const PSRAMAllocator<U>&) noexcept {}
77
78 /**
79 * @brief Allocate memory for n objects of type T
80 *
81 * Attempts to allocate memory in the following priority order:
82 * 1. PSRAM using heap_caps_aligned_calloc (if available)
83 * 2. Aligned allocation using aligned_alloc (C11)
84 * 3. Standard calloc as fallback
85 *
86 * All allocated memory is zero-initialized.
87 *
88 * @param n Number of objects of type T to allocate
89 * @return Pointer to allocated memory
90 * @throws std::bad_alloc if allocation fails
91 *
92 * @note Memory is aligned to alignof(T)
93 * @note On ESP32 with PSRAM, prefers external PSRAM allocation
94 */
95 T* allocate(std::size_t n) {
96 if (n == 0) return nullptr;
97 // total bytes
98 std::size_t bytes = n * sizeof(T);
99 // Prefer PSRAM: use heap_caps_aligned_calloc if available. Request
100 // alignment equal to alignof(T).
101 void* p = nullptr;
102#if defined(MALLOC_CAP_SPIRAM)
103 // heap_caps_aligned_calloc(alignment, nmemb, size, caps)
104 p = heap_caps_aligned_calloc(alignof(T), n, sizeof(T),
105 (unsigned)MALLOC_CAP_SPIRAM);
106#endif
107 if (!p) {
108 // Fallback: try aligned allocation where available
109#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
110 // aligned_alloc requires size to be multiple of alignment
111 std::size_t align = alignof(T);
112 std::size_t alloc_size = bytes;
113 if (align > 1) {
114 std::size_t rem = alloc_size % align;
115 if (rem) alloc_size += (align - rem);
116 }
117 p = aligned_alloc(align, alloc_size);
118 if (p) {
119 // zero initialize
120 std::memset(p, 0, bytes);
121 } else
122#endif
123 {
124 // last resort: calloc
125 p = std::calloc(n, sizeof(T));
126 }
127 }
128
129 if (!p) throw std::bad_alloc();
130 return static_cast<T*>(p);
131 }
132
133 /**
134 * @brief Deallocate memory previously allocated by this allocator
135 *
136 * Frees memory that was allocated by allocate(). Uses heap_caps_free
137 * which works for both PSRAM and regular heap allocations.
138 *
139 * @param p Pointer to memory to deallocate (can be nullptr)
140 * @param n Number of objects (ignored, but required by allocator interface)
141 *
142 * @note Safe to call with nullptr
143 * @note The size parameter n is ignored as heap_caps_free doesn't need it
144 */
145 void deallocate(T* p, std::size_t /*n*/) noexcept {
146 if (!p) return;
147 heap_caps_free(p);
148 }
149
150 /**
151 * @brief Rebind allocator to different type
152 *
153 * Provides the rebind mechanism required by STL allocators.
154 * Allows containers to allocate different types using the same allocator.
155 *
156 * @tparam U The type to rebind to
157 */
158 template <class U>
159 struct rebind {
160 /** @brief The rebound allocator type */
161 typedef PSRAMAllocator<U> other;
162 };
163};
164
165} // namespace tinygpu
166
167#endif