このページでは

C

Qt Quick におけるメモリ割り当てUltralite プラットフォームの抽象化

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が利用可能であることを前提としています。ただし、カスタムプラットフォームへの移植では、他のメモリ領域を使用することも考えられます。

`MemoryAllocator::acquire ` 関数のオーバーライドは、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;
    }
}

ここでは、画像の割り当てには前述のメモリアロケータの例を使用しますが、その他の割り当てについてはデフォルトの実装にフォールバックします。デフォルトの実装は、Platform::qul_mallocPlatform::qul_realloc 、および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;
};

高速なメモリ割り当て

Platform::StackAllocator を使用すると、通常の割り当てよりも高速に短期メモリを割り当てることができます。使用例はits class documentation に掲載されています。

アロケータを準備するために、静的メモリを事前割り当てします。これは、一定量のメモリを確保し、l{Qul::Platform::}{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 ライセンスで利用可能です。
詳細はこちらをご覧ください。