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_mallocQul::Platform::qul_reallocQul::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::printHeapStatsQul::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许可协议下提供。
了解详情。