C
Qt Quick Ultralite平台抽象中的内存分配
所有来自Qt Quick Ultralite核心库的内存分配都使用一组专用的函数,允许您根据要移植的平台处理动态内存分配请求。
基本内存分配
Qt Quick Ultralite核心库使用以下函数处理小型内存分配
您可以将这些调用转发到以下平台特定函数
void *qul_malloc(std::size_t size) { return std::malloc(size); } void qul_free(void *ptr) { std::free(ptr); } void *qul_realloc(void *ptr, size_t s) { return std::realloc(ptr, s); }
内存分配器API
Qt Quick Ultralite核心库使用内存分配器API进行大容量内存分配。此API提供了更多控制,用于确定用于不同目的的内存分配方式和位置。例如,在某些平台上,将图像数据存储在VRAM中可能是有意义的。该API还要求在写入之前和之后显式获取和释放Qt Quick Ultralite核心库的内存,以便平台执行任何必要的同步。
示例内存分配器
以下是一个示例内存分配器的样子
class ExampleMemoryAllocator : public PlatformInterface::MemoryAllocator { public: void *allocate(std::size_t size, UsagePattern) QUL_DECL_OVERRIDE { void *ptr = NULL; // ptr = HW_VideoMemAlloc(size); return ptr; } void *reallocate(void *ptr, std::size_t size) QUL_DECL_OVERRIDE { void *new_ptr = NULL; // new_ptr = HW_VideoMemRealloc(ptr, size); return new_ptr; } void free(void *ptr) QUL_DECL_OVERRIDE { // HW_VideoMemFree(ptr); } void acquire(void * /* ptr */, std::size_t /* offset */, std::size_t /* size */) QUL_DECL_OVERRIDE { // WaitForLcdUpdateComplete(); } };
在这个例子中,我们假设有一个可用的硬件API可用于分配视频内存,而不是正常内存。尽管,自定义平台移植可能会选择使用其他内存区域。
在调用Qul::PlatformInterface::MemoryAllocator::acquire函数覆盖写入使用Qul::PlatformInterface::MemoryAllocator API分配的内存缓冲区之前。某些硬件可能具有有限的内存带宽,例如,当LCD显示控制器扫描帧缓冲区时不能写入SDRAM。在这种情况下,平台可以选择等待LCD更新完成后再允许向SDRAM写入。
以下是PlatformContext::memoryAllocator函数的示例实现
PlatformInterface::MemoryAllocator *ExamplePlatform::memoryAllocator( PlatformInterface::MemoryAllocator::AllocationType type) { static ExampleMemoryAllocator exampleMemoryAllocator; static ExampleReversePreloadAllocator<4> examplePreloadAllocator(preloadSdramEnd, preloadSdramStart); static PlatformInterface::MemoryAllocator defaultMemoryAllocator; switch (type) { case PlatformInterface::MemoryAllocator::Image: return &exampleMemoryAllocator; case PlatformInterface::MemoryAllocator::DefaultPreload: return &examplePreloadAllocator; default: return &defaultMemoryAllocator; } }
在此,我们为图像分配使用我们的示例内存分配器,但对于其他分配则回退到默认实现。默认实现将控制转发给Qul::Platform::qul_malloc、Qul::Platform::qul_realloc和Qul::Platform::qul_free函数。
示例反向预加载分配器
在平台初始化过程中,可预加载的资源只加载一次,因此我们可以使用更简单的分配器来完成此目的。例如,内存重新分配和释放不是必需的,因此没有必要支持。通过使用反向分配器,预加载内存从段的末尾开始,增长到段的开始处。这可以避免预加载内存与堆内存以相同方向扩展的问题。
以下是一个示例反向预加载分配器的样子
template<std::size_t ALIGNMENT> class ExampleReversePreloadAllocator : public PlatformInterface::MemoryAllocator { public: ExampleReversePreloadAllocator(void *endAddress, void *startAddress = NULL) : m_currentAddress(endAddress) , m_startAddress(startAddress) {} void *allocate(std::size_t size, UsagePattern) QUL_DECL_OVERRIDE { m_currentAddress = static_cast<char *>(m_currentAddress) - size; m_currentAddress = static_cast<char *>(m_currentAddress) - (reinterpret_cast<uintptr_t>(m_currentAddress) % ALIGNMENT); if (m_startAddress && m_currentAddress < m_startAddress) return NULL; return m_currentAddress; } // Reallocating is not supported in preloading void *reallocate(void *, std::size_t) QUL_DECL_OVERRIDE { QUL_ASSERT(false, QulError_PreloadAllocator_UnsupportedOperation); return NULL; }; // Preallocated assets are not freed void free(void *) QUL_DECL_OVERRIDE { QUL_ASSERT(false, QulError_PreloadAllocator_UnsupportedOperation); }; void acquire(void *, std::size_t, std::size_t) QUL_DECL_OVERRIDE { QUL_ASSERT(false, QulError_PreloadAllocator_UnsupportedOperation); }; void release(void *, std::size_t, std::size_t) QUL_DECL_OVERRIDE { QUL_ASSERT(false, QulError_PreloadAllocator_UnsupportedOperation); }; private: void *m_currentAddress; const void *m_startAddress; };
内存统计
平台提供获取内存统计信息的函数,例如堆和栈大小。
必须实现Qul::Platform::printHeapStats和Qul::Platform::printStackStats函数来打印此类信息。如果你的平台提供了mallinfo()
函数,你也可以使用它。
void printHeapStats(void) { struct mallinfo mi = mallinfo(); PlatformInterface::log("Heap: %u/%u (in-use/total)\r\n", mi.uordblks, mi.arena); } void printStackStats(void) { // Replace this with actual measuring for your platform uint32_t maxUsedStackSize = 0; PlatformInterface::log("Stack: %u (peak)\r\n", maxUsedStackSize); }
void printHeapStats(void) { struct mallinfo mi = __iar_dlmallinfo(); PlatformInterface::log("Heap: %u/%u (in-use/total)\r\n", mi.uordblks, mi.arena); } void printStackStats(void) { // Replace this with actual measuring for your platform uint32_t maxUsedStackSize = 0; PlatformInterface::log("Stack: %u (peak)\r\n", maxUsedStackSize); }
快速内存分配
使用Qul::Platform::StackAllocator可以比常规分配更快地分配短期内存。示例用法可以在其类文档中找到。
预分配静态内存以准备工作分配器。这是通过预留一定量的内存并初始化StackAllocator
的静态成员来完成的。
static char qul_scratch_buffer[16 * 1024]; char *StackAllocator::m_buffer = qul_scratch_buffer; char *StackAllocator::m_top = StackAllocator::m_buffer; int32_t StackAllocator::m_size = sizeof(qul_scratch_buffer);
早期示例中的代码预留了16KB的缓冲区,并将它的指针和大小设置到了StackAllocator
。
注意:堆栈分配器必须初始化,以供Qt Quick Ultralite核心库使用。
在某些Qt许可协议下提供。
了解详情。