C
ハードウェアレイヤーのサポートの実装
一部のハードウェアプラットフォームではレイヤーがサポートされており、これにより、複数の独立したビジュアルレイヤーにコンテンツを描画し、ハードウェアにそれらを合成させることができます。また、ハードウェアによっては、Flash内の画像データから直接レイヤーのコンテンツを定義できる場合もあります。これにより、静的な背景コンテンツには画像を、動的なコンテンツにはフレームバッファを使用することで、揮発性メモリの使用量を削減できるはずです。
レイヤーエンジンインターフェース
まず、Qt Quick Ultraliteコアがプラットフォームレイヤーの割り当て、更新、および解放を行うために使用するPlatformInterface::LayerEngine インターフェースを実装します。
class ExampleLayerEngine : public PlatformInterface::LayerEngine { public: ItemLayer *allocateItemLayer(const PlatformInterface::Screen *, const ItemLayerProperties &props, SpriteLayer *spriteLayer = NULL) QUL_DECL_OVERRIDE; ImageLayer *allocateImageLayer(const PlatformInterface::Screen *, const ImageLayerProperties &props, SpriteLayer *spriteLayer = NULL) QUL_DECL_OVERRIDE; SpriteLayer *allocateSpriteLayer(const PlatformInterface::Screen *, const SpriteLayerProperties &props) QUL_DECL_OVERRIDE; void deallocateItemLayer(ItemLayer *layer) QUL_DECL_OVERRIDE; void deallocateImageLayer(ImageLayer *layer) QUL_DECL_OVERRIDE; void deallocateSpriteLayer(SpriteLayer *layer) QUL_DECL_OVERRIDE; void updateItemLayer(ItemLayer *layer, const ItemLayerProperties &props) QUL_DECL_OVERRIDE; void updateImageLayer(ImageLayer *layer, const ImageLayerProperties &props) QUL_DECL_OVERRIDE; void updateSpriteLayer(SpriteLayer *layer, const SpriteLayerProperties &props) QUL_DECL_OVERRIDE; };
サンプルソースファイル `platform/boards/qt/example-baremetal/examplelayerengine.cpp ` には、レイヤーエンジンを利用するために、現在のプラットフォームコンテキスト内の同名の関数にあるコードを置き換える必要がある、PlatformContext::beginFrame 、PlatformContext::endFrame 、およびPlatformContext::presentFrame の更新された実装が含まれています。これらは、このページの下部に記載されています。
さらに、PlatformContext::layerEngine メソッドを実装し、Qul::PlatformInterface::LayerEngine のインスタンスへのポインタを返す必要があります。この場合、それは静的に割り当てられたExampleLayerEngine のインスタンスとなります:
PlatformInterface::LayerEngine *ExamplePlatform::layerEngine() { static ExampleLayerEngine layerEngine; return &layerEngine; }
レイヤーエンジンインターフェースの実装
レイヤーエンジンインターフェースを実装するには、Qul::PlatformInterface::LayerEngine::ItemLayer 、Qul::PlatformInterface::LayerEngine::ImageLayer 、およびQul::PlatformInterface::LayerEngine::SpriteLayer という不透明型のサブクラスを作成します。これらのサブクラスは、プラットフォーム固有のレイヤーデータを格納するために使用できます。
スプライトレイヤー内にある画像レイヤーやアイテムレイヤーは、ルートレイヤーである画像レイヤーやアイテムレイヤーとは異なる実装が必要です。サンプルプラットフォームでは、ExampleImageLayer クラスとExampleImageSprite クラスが、それぞれルート画像レイヤーとスプライト画像レイヤーを表すために使用されています。同様に、ExampleItemLayer クラスとExampleItemSprite クラスはアイテムレイヤーを表し、ExampleSpriteLayer クラスはスプライトレイヤーを表します。
以下のスニペットは、サンプルプラットフォームにおいてこれらがどのように割り当てられているかを示しています。複数の画面に対応したい場合は、Qul::PlatformInterface::Screen ポインタを考慮して、レイヤーを正しい画面に表示する必要があります。
PlatformInterface::LayerEngine::ItemLayer *ExampleLayerEngine::allocateItemLayer(const PlatformInterface::Screen *, const ItemLayerProperties &props, SpriteLayer *spriteLayer) { ItemLayer *layer; if (spriteLayer) { ExampleSpriteLayer *exampleSpriteLayer = static_cast<ExampleSpriteLayer *>(spriteLayer); layer = new ExampleItemSprite(props, exampleSpriteLayer); } else { layer = new ExampleItemLayer(props); } return layer; } PlatformInterface::LayerEngine::ImageLayer *ExampleLayerEngine::allocateImageLayer(const PlatformInterface::Screen *, const ImageLayerProperties &props, SpriteLayer *spriteLayer) { ImageLayer *layer; if (spriteLayer) { ExampleSpriteLayer *exampleSpriteLayer = static_cast<ExampleSpriteLayer *>(spriteLayer); layer = new ExampleImageSprite(props, exampleSpriteLayer); } else { layer = new ExampleImageLayer(props); } return layer; } PlatformInterface::LayerEngine::SpriteLayer *ExampleLayerEngine::allocateSpriteLayer(const PlatformInterface::Screen *, const SpriteLayerProperties &props) { return new ExampleSpriteLayer(props); }
Qul::PlatformInterface::LayerEngine::ImageLayer という不透明型を安全にダウンキャストするために、ExampleImageLayer およびExampleImageSprite はExampleImageLayerBase を継承しており、 はさらにQul::PlatformInterface::LayerEngine::ImageLayer を継承しています:
struct ExampleImageLayerBase : public Qul::PlatformInterface::LayerEngine::ImageLayer { virtual ~ExampleImageLayerBase() {} virtual void updateProperties(const Qul::PlatformInterface::LayerEngine::ImageLayerProperties &props) = 0; };
これには、正しいサブクラスのデストラクタが呼び出されるようにするための仮想デストラクタと、画像レイヤーのプロパティが変更された際にそれを更新するための仮想関数が含まれています。
同様に、ExampleItemLayer およびExampleSpriteLayer が継承するExampleItemLayerBase があります:
struct ExampleItemLayerBase : public Qul::PlatformInterface::LayerEngine::ItemLayer { ExampleItemLayerBase(const Qul::PlatformInterface::LayerEngine::ItemLayerProperties &props); virtual ~ExampleItemLayerBase() {} virtual void updateProperties(const Qul::PlatformInterface::LayerEngine::ItemLayerProperties &props) = 0; virtual unsigned char *getNextDrawBuffer() = 0; virtual void swap() = 0; Qul::PlatformInterface::DrawingDevice drawingDevice; unsigned int lastSwapFrame; unsigned int targetFrame; };
仮想デストラクタとupdateProperties メソッドに加え、次のドローバッファを取得するための仮想メソッドや、1フレームのレンダリングが完了した際にアイテムレイヤーのバッファをスワップするための仮想メソッドも用意されています。また、動的なリフレッシュ間隔をサポートし、アイテムレイヤーの更新対象となるフレームを追跡するための変数も用意されています。
仮想デストラクタを使用することで、適切なプラットフォームクラスへダウンキャストし、delete を呼び出すことで、メモリ解放メソッドを実装できます:
void ExampleLayerEngine::deallocateItemLayer(PlatformInterface::LayerEngine::ItemLayer *layer) { delete static_cast<ExampleItemLayerBase *>(layer); } void ExampleLayerEngine::deallocateImageLayer(PlatformInterface::LayerEngine::ImageLayer *layer) { delete static_cast<ExampleImageLayerBase *>(layer); } void ExampleLayerEngine::deallocateSpriteLayer(PlatformInterface::LayerEngine::SpriteLayer *layer) { delete static_cast<ExampleSpriteLayer *>(layer); }
レイヤーのプロパティを更新するために使用されるメソッドも同様に実装されており、適切なサブクラスへフォワードされます:
void ExampleLayerEngine::updateItemLayer(PlatformInterface::LayerEngine::ItemLayer *layer, const ItemLayerProperties &props) { static_cast<ExampleItemLayerBase *>(layer)->updateProperties(props); } void ExampleLayerEngine::updateImageLayer(PlatformInterface::LayerEngine::ImageLayer *layer, const ImageLayerProperties &props) { static_cast<ExampleImageLayerBase *>(layer)->updateProperties(props); } void ExampleLayerEngine::updateSpriteLayer(PlatformInterface::LayerEngine::SpriteLayer *layer, const SpriteLayerProperties &props) { static_cast<ExampleSpriteLayer *>(layer)->updateProperties(props); }
ハードウェア層の基底クラス
ExampleHardwareLayer クラスは、ExampleItemLayer 、ExampleImageLayer 、およびExampleSpriteLayer クラスの基底クラスです。このクラスは、ルート(スプライト以外の)ハードウェアレイヤーの初期化および更新方法を示しています。
ダミーのハードウェアアクセラレーションAPIを使用した場合、コンストラクタは次のような形になるでしょう:
struct ExampleHardwareLayer { Qul::PlatformInterface::LayerEngine::LayerPropertiesBase props; Qul::PlatformInterface::Size size; // hw_layer_t hwLayer; enum Type { SpriteLayer, FramebufferLayer }; ExampleHardwareLayer(const Qul::PlatformInterface::LayerEngine::LayerPropertiesBase &p, const Qul::PlatformInterface::Size &s, HwPixelFormat pixelFormat, Type type) : props(p) , size(s) { // hw_layer_create_data_t layerCreateData; // layerCreateData.opacity = p.opacity; // layerCreateData.x = p.position.x(); // layerCreateData.y = p.position.y(); // layerCreateData.z = p.z; // layerCreateData.width = size.width(); // layerCreateData.height = size.height(); // layerCreateData.pixelFormat = pixelFormat; // layerCreateData.type = type == SpriteLayer ? HW_LAYER_TYPE_SPRITE : HW_LAYER_TYPE_FRAMEBUFFER; // HW_LayerCreate(&hwLayer, &layerData); }
ここでは、ハードウェアレイヤーのすべてのプロパティを設定し、ハードウェアレイヤーAPI関数を呼び出してハードウェアレイヤーを作成します。
次に、updateProperties() は、サイズ、位置、不透明度、有効状態など、あらゆるレイヤータイプに共通する基本プロパティに基づいて、ハードウェアレイヤーを更新します:
void updateProperties(const Qul::PlatformInterface::LayerEngine::LayerPropertiesBase &p, const Qul::PlatformInterface::Size &s) { if (size != s) { // HW_LayerResize(&hwLayer, p.size.width(), p.size.height()); } if (props.position != p.position || props.z != p.z) { // HW_LayerMove(&hwLayer, p.position.x(), p.position.y(), p.z); } if (props.opacity != p.opacity) { // HW_LayerSetOpacity(&hwLayer, p.opacity); } if (p.enabled != props.enabled) { if (p.enabled) { // HW_LayerEnable(&hwLayer); } else { // HW_LayerDisable(&hwLayer); } } size = s; props = p; }
デストラクタは、ハードウェアレイヤーのリソースを削除します:
~ExampleHardwareLayer() { // HW_LayerDelete(&hwLayer); }
アイテム層のクラス
まず、ExampleItemLayer クラスは、指定されたハードウェアレイヤーのフレームバッファを割り当て、破棄します:
struct ExampleItemLayer : public ExampleItemLayerBase, public ExampleHardwareLayer { ExampleItemLayer(const Qul::PlatformInterface::LayerEngine::ItemLayerProperties &props) : ExampleItemLayerBase(props) , ExampleHardwareLayer(props, props.size, toHwPixelFormat(props.colorDepth), ExampleHardwareLayer::FramebufferLayer) { // Allocate double buffers for hardware framebuffer layer // HW_CreateFramebuffers(&hwLayer, props.size.width(), props.size.height(), toHwPixelFormat(props.colorFormat), 2); } ~ExampleItemLayer() { // HW_DestroyFramebuffers(&hwLayer, props.size.width(), props.size.height(), toHwPixelFormat(props.colorFormat), 2); }
updateProperties メソッドは、基底実装であるExampleHardwareLayer へ処理を委譲します:
virtual void updateProperties(const Qul::PlatformInterface::LayerEngine::ItemLayerProperties &props) QUL_DECL_OVERRIDE { ExampleHardwareLayer::updateProperties(props, props.size); }
getNextDrawBuffer は、バックバッファの準備が整うまで待機してからそのアドレスを返し、swap はハードウェア層のメソッドを呼び出してフロントバッファとバックバッファを入れ替えます:
virtual unsigned char *getNextDrawBuffer() QUL_DECL_OVERRIDE { unsigned char *bits = NULL; do { // bits = HW_GetNextFramebuffer(&hwLayer); } while (!bits); return bits; } virtual void swap() QUL_DECL_OVERRIDE { // HW_FinishRendering(); // HW_SwapFramebuffers(&hwLayer); }
スプライトデータのアドレスが任意の有効なポインタであると仮定すると、この例のプラットフォームにおけるExampleItemSprite クラスでは、2つのフレームバッファは手動で割り当てられ、解放されます:
struct ExampleItemSprite : public ExampleItemLayerBase { ExampleItemSprite(const Qul::PlatformInterface::LayerEngine::ItemLayerProperties &p, ExampleSpriteLayer *spriteLayer) : ExampleItemLayerBase(props) , didCreate(false) , frontBufferIndex(0) , spriteLayer(spriteLayer) , props(p) { // For item sprites we manually allocate the framebuffers for (int i = 0; i < 2; ++i) framebuffers[i] = (unsigned char *) qul_malloc(p.size.width() * p.size.height() * bytesPerPixel(p.colorDepth)); } ~ExampleItemSprite() { for (int i = 0; i < 2; ++i) qul_free(framebuffers[i]); if (didCreate) { // HW_SpriteDelete(&hwSprite); } }
更新が必要なスプライト関連のプロパティは、位置と、スプライトが有効か無効かです:
virtual void updateProperties(const Qul::PlatformInterface::LayerEngine::ItemLayerProperties &p) QUL_DECL_OVERRIDE { if (!didCreate) return; if (props.position != p.position || props.z != p.z) { // HW_SpriteMove(&hwSprite, p.position.x(), p.position.y(), p.z); } if (props.enabled != p.enabled) { if (p.enabled) { // HW_SpriteEnable(&hwSprite); } else { // HW_SpriteDisable(&hwSprite); } } props = p; }
getNextDrawBuffer はバックバッファへのポインタを返し、swap のオーバーライドでは、スプライトデータを更新するか、まだ作成されていない場合はスプライトを作成します:
virtual unsigned char *getNextDrawBuffer() QUL_DECL_OVERRIDE { return framebuffers[!frontBufferIndex]; } virtual void swap() QUL_DECL_OVERRIDE { // HW_FinishRendering(); frontBufferIndex = !frontBufferIndex; if (didCreate) { // HW_SpriteSetData(&hwSprite, framebuffers[frontBufferIndex]); } else { create(); didCreate = true; } }
ハードウェアスプライトの作成は、次のようなものになります。
void create() { // hw_sprite_create_data_t spriteCreateData; // spriteCreateData.x = props.position.x(); // spriteCreateData.y = props.position.y(); // spriteCreateData.z = props.z; // spriteCreateData.width = props.size.width(); // spriteCreateData.height = props.size.height(); // spriteCreateData.pixelFormat = toHwPixelFormat(props.colorDepth); // spriteCreateData.data = framebuffers[frontBufferIndex]; // spriteCreateData.layer = &spriteLayer->hwLayer; // HW_SpriteCreate(&hwSprite, &spriteLayer->hwLayer); }
画像レイヤークラス
ExampleImageLayer クラスは、生成やプロパティの更新において基底クラスであるExampleHardwareLayer に依存しており、さらにテクスチャデータをフレームバッファとして直接設定します:
struct ExampleImageLayer : public ExampleImageLayerBase, public ExampleHardwareLayer { ExampleImageLayer(const Qul::PlatformInterface::LayerEngine::ImageLayerProperties &p) : ExampleHardwareLayer(p, p.texture.size(), toHwPixelFormat(p.texture.format()), ExampleHardwareLayer::FramebufferLayer) { // HW_LayerSetFramebuffer(&hwLayer, p.texture.data(), p.texture.bytesPerLine()); } void updateProperties(const Qul::PlatformInterface::LayerEngine::ImageLayerProperties &p) QUL_DECL_OVERRIDE { ExampleHardwareLayer::updateProperties(p, p.texture.size()); // HW_LayerSetFramebuffer(&hwLayer, p.texture.data(), p.texture.bytesPerLine()); }
Qt Quick Ultraliteコアは、画像レイヤーが表示されている限り、テクスチャデータが有効な状態を維持するように保証します。
ExampleImageSprite の場合、ハードウェアスプライトはコンストラクタ内で即座に作成でき、デストラクタ内で破棄できます:
struct ExampleImageSprite : public ExampleImageLayerBase { ExampleImageSprite(const Qul::PlatformInterface::LayerEngine::ImageLayerProperties &p, ExampleSpriteLayer *spriteLayer) : props(p) { // hw_sprite_create_data_t spriteCreateData; // spriteCreateData.x = p.position.x(); // spriteCreateData.y = p.position.y(); // spriteCreateData.z = p.z; // spriteCreateData.width = p.size.width(); // spriteCreateData.height = p.size.height(); // spriteCreateData.pixelFormat = toHwPixelFormat(p.texture.format()); // spriteCreateData.data = p.texture.data(); // spriteCreateData.layer = &spriteLayer->hwLayer; // HW_SpriteCreate(&hwSprite, &spriteLayer->hwLayer); } ~ExampleImageSprite() { // HW_SpriteDelete(&hwSprite); }
updateProperties のオーバーライドは、アイテムスプライトの場合と同様ですが、テクスチャデータが変更されている可能性がある点が異なります:
void updateProperties(const Qul::PlatformInterface::LayerEngine::ImageLayerProperties &p) QUL_DECL_OVERRIDE { if (props.texture.data() != p.texture.data()) { // HW_SpriteSetData(&hwSprite, p.texture.data()); } if (props.position != p.position || props.z != p.z) { // HW_SpriteMove(&hwSprite, p.position.x(), p.position.y(), p.z); } if (props.enabled != p.enabled) { if (p.enabled) { // HW_SpriteEnable(&hwSprite); } else { // HW_SpriteDisable(&hwSprite); } } props = p; }
スプライトレイヤークラス
スプライトレイヤーは、アイテムスプライトや画像スプライトを格納するための単純なコンテナです。位置、不透明度、サイズのプロパティは基底クラスであるExampleHardwareLayer によって処理されるため、ExampleSpriteLayer クラスは特別な処理を行う必要はありません。
struct ExampleSpriteLayer : public Qul::PlatformInterface::LayerEngine::SpriteLayer, public ExampleHardwareLayer { ExampleSpriteLayer(const Qul::PlatformInterface::LayerEngine::SpriteLayerProperties &props) : ExampleHardwareLayer(props, props.size, toHwPixelFormat(props.colorDepth), ExampleHardwareLayer::SpriteLayer) {} void updateProperties(const Qul::PlatformInterface::LayerEngine::SpriteLayerProperties &props) { ExampleHardwareLayer::updateProperties(props, props.size); } static void *operator new(std::size_t size) { return qul_malloc(size); } static void operator delete(void *ptr) { qul_free(ptr); } };
プラットフォームのbeginFrame 、endFrame 、およびpresentFrame API
PlatformContext::beginFrame の実装は、レイヤーエンジンを利用するコードに置き換える必要があります。
// This function has to replace your PlatformContext::beginFrame PlatformInterface::DrawingDevice *beginFrame(const PlatformInterface::LayerEngine::ItemLayer *layer, const PlatformInterface::Rect &, int refreshInterval) { ExampleItemLayerBase *itemLayer = const_cast<ExampleItemLayerBase *>( static_cast<const ExampleItemLayerBase *>(layer)); if (itemLayer->lastSwapFrame == currentFrame) { // waitForVblank(); } if (refreshInterval == -1) itemLayer->targetFrame = currentFrame + 1; else itemLayer->targetFrame = itemLayer->lastSwapFrame + refreshInterval; unsigned char *bits = itemLayer->getNextDrawBuffer(); // Tell the HW drawing API which framebuffer to render to // HW_FramebufferSet(bits, drawingDevice.width(), drawingDevice.height(), toHwPixelFormat(drawingDevice.format())); // The drawing device also needs the framebuffer pointer for the CPU rendering fallbacks to work itemLayer->drawingDevice.setBits(bits); return &itemLayer->drawingDevice; }
beginFrame メソッドは、バックバッファがレンダリング可能な状態にあることを保証します。このメソッドは、前回のバッファスワップ以降に垂直リフレッシュが発生したかどうかを確認し、リフレッシュ間隔に基づいて次のスワップのターゲットフレームを設定します。また、レンダリング可能なバックバッファへのポインタを取得し、ハードウェア API およびQul::PlatformInterface::DrawingDevice に対して、そのアドレスへレンダリングするよう指示します。
次に、PlatformContext::endFrame メソッドの実装を、レイヤーエンジンを利用するコードに置き換える必要があります:
bool waitingForTargetFrame(ExampleItemLayerBase *layer) { const unsigned int delta = layer->targetFrame - currentFrame; // guard against wrap-around (swap intervals above 0x100 are unlikely) return delta != 0 && delta < 0x100; } // This function has to replace your PlatformContext::endFrame void endFrame(const PlatformInterface::LayerEngine::ItemLayer *layer) { ExampleItemLayerBase *itemLayer = const_cast<ExampleItemLayerBase *>( static_cast<const ExampleItemLayerBase *>(layer)); while (waitingForTargetFrame(itemLayer)) ; itemLayer->swap(); itemLayer->lastSwapFrame = currentFrame; }
ターゲットフレームのレイヤー更新が完了するまで待機し、その後swap メソッドを呼び出して、アイテムレイヤーのフロントバッファとバックバッファをスワップします。
最後に、PlatformContext::presentFrame メソッドの実装を、レイヤーエンジンを利用するコードに置き換える必要があります:
// This function has to replace your PlatformContext::presentFrame FrameStatistics presentFrame(const PlatformInterface::Screen *screen, const PlatformInterface::Rect &rect) { static unsigned int lastFrame = 0xffffffff; while (currentFrame == lastFrame) { // The device may yield or go into sleep mode } lastFrame = currentFrame; PlatformInterface::Rgba32 color = screen->backgroundColor(); // HW_SetScreenBackgroundColor(color.red(), color.blue(), color.green()); // No frame skip compensation implemented for layers return FrameStatistics(); }
これにより、アイテムレイヤーがレンダリングされていない場合でも、更新が画面の更新レートに合わせて調整されます。さらに、ハードウェア API を使用して画面の背景色を設定します。
Qt Quick Ultralite コアにおけるフレームスキップの補正を回避するため、デフォルトコンストラクタで生成された `Platform::FrameStatistics ` オブジェクトが返されます。
特定のQtライセンスの下で利用可能です。
詳細はこちら。