C
ハードウェアレイヤーサポートの実装
つまり、複数の独立したビジュアルレイヤーにコンテンツを描画し、ハードウェアにそれらをブレンドさせることができます。ハードウェアはまた、フラッシュ内の画像データから直接レイヤーコンテンツを定義できるかもしれません。これは、静的な背景コンテンツには画像を使い、動的なコンテンツにはフレームバッファを使うことで、揮発性メモリの使用量を減らすのに役立つはずです。
レイヤーエンジンインターフェイス
最初に、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 の更新された実装が含まれており、レイヤー・エンジンを使用するために、現在のプラットフォーム・コンテキストで同じ関数のコードを置き換える必要があります。これらはこのページの後半で紹介します。
さらに、Qul::PlatformInterface::LayerEngine のインスタンスへのポインタを返すように、PlatformContext::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 メソッドに加えて、次の描画バッファを取得する仮想メソッドと、フレームがレンダリングされたらアイテムレイヤーのバッファを入れ替える仮想メソッドもあります。また、動的な更新間隔をサポートし、アイテムレイヤーの更新が意図するフレームを追跡するための変数もあります。
仮想デストラクタを使えば、適切なプラットフォーム・クラスにダウンキャストして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 ライセンスの下で利用可能です。
詳細を確認してください。