C
ハードウェアアクセラレーションによるブレンディング
ハードウェア・アクセラレーションを実装するには、PlatformInterface::DrawingEngine をサブクラス化し、プラットフォームがアクセラレーションできる関数をオーバーライドする。
class ExampleDrawingEngine : public PlatformInterface::DrawingEngine { public: void blendRect(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Rect &rect, PlatformInterface::Rgba32 color, BlendMode blendMode) QUL_DECL_OVERRIDE; void blendRoundedRect(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Rect &rect, const PlatformInterface::Rect &clipRect, PlatformInterface::Rgba32 color, int radius, BlendMode blendMode) QUL_DECL_OVERRIDE; void blendImage(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Point &pos, const PlatformInterface::Texture &source, const PlatformInterface::Rect &sourceRect, int sourceOpacity, BlendMode blendMode) QUL_DECL_OVERRIDE; void blendAlphaMap(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Point &pos, const PlatformInterface::Texture &source, const PlatformInterface::Rect &sourceRect, PlatformInterface::Rgba32 color, BlendMode blendMode) QUL_DECL_OVERRIDE; void blendTransformedImage(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Transform &transform, const PlatformInterface::RectF &destinationRect, const PlatformInterface::Texture &source, const PlatformInterface::RectF &sourceRect, const PlatformInterface::Rect &clipRect, int sourceOpacity, BlendMode blendMode); void blendTransformedAlphaMap(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Transform &transform, const PlatformInterface::RectF &destinationRect, const PlatformInterface::Texture &source, const PlatformInterface::RectF &sourceRect, const PlatformInterface::Rect &clipRect, PlatformInterface::Rgba32 color, BlendMode blendMode); void synchronizeForCpuAccess(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Rect &rect) QUL_DECL_OVERRIDE; PlatformInterface::DrawingEngine::Path *allocatePath(const PlatformInterface::PathData *pathData, PlatformInterface::PathFillRule fillRule) QUL_DECL_OVERRIDE; void setStrokeProperties(PlatformInterface::DrawingEngine::Path *path, const PlatformInterface::StrokeProperties &strokeProperties) QUL_DECL_OVERRIDE; void blendPath(PlatformInterface::DrawingDevice *drawingDevice, PlatformInterface::DrawingEngine::Path *path, const PlatformInterface::Transform &transform, const PlatformInterface::Rect &clipRect, const PlatformInterface::Brush *fillBrush, const PlatformInterface::Brush *strokeBrush, int sourceOpacity, PlatformInterface::DrawingEngine::BlendMode blendMode) QUL_DECL_OVERRIDE; PlatformInterface::RenderHints supportedRenderHints() const QUL_DECL_OVERRIDE; };
DrawingEngine::blendRect,DrawingEngine::blendRoundedRect,DrawingEngine::blendImage,DrawingEngine::blendAlphaMap,DrawingEngine::blendTransformedImage,DrawingEngine::blendTransformedAlphaMap 関数のいずれかがオーバーライドされていない場合、デフォルトの実装が使用されます。この関数は、DrawingDevice::fallbackDrawingEngine の対応するフォールバック実装を呼び出す前に、synchronizeForCpuAccess() を呼び出す。プラットフォームがあるブレンド関数を部分的に高速化できる場合、たとえば、指定されたブレンドモードまたは不透明度パラメータに対してのみ高速化できる場合、それ自身がfallbackDrawingEngine を使用して、特定のパラメータセットに対してフォールバックすることができます。
警告 fallbackDrawingEngine を使用してクラッシュが発生した場合、initializePlatform() 内でPlatformInterface::initializeArgb32CpuFallbackDrawingEngine() などを呼び出していないことが原因である可能性があります。
注意 : blendImage 関数がデフォルトの実装でソフトウェアレンダリングを使用している場合、またはfallbackDrawingEngine を使用している場合、ソース画像は ARGB32_Premultiplied 形式であると見なされます。blendImageで他のフォーマットのブレンドを有効にするには、QUL_PLATFORM_DEFAULT_RESOURCE_ALPHA_OPTIONSをAlways に設定します。
ブレンド関数の実際の実装は、ハードウェアアクセラレーションAPIによって大きく異なる可能性があります。これらの関数を実装する作業を簡単にするために、ダミーのハードウェア加速APIをデモ用に使用します。例えば、DrawingEngine::blendRect の実装は以下のようになります:
void ExampleDrawingEngine::blendRect(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Rect &rect, PlatformInterface::Rgba32 color, BlendMode blendMode) { // Implement rectangle blending here // If only blitting is supported by the hardware, this is how to use the // fallback drawing engine for the blending path. if (color.alpha() != 255 && blendMode != BlendMode_SourceOver) { synchronizeForCpuAccess(drawingDevice, rect); drawingDevice->fallbackDrawingEngine()->blendRect(drawingDevice, rect, color, blendMode); return; } // HW_SetColor(1.0f, 1.0f, 1.0f, sourceOpacity * (1 / 256.0f)); // HW_BlitRect(toHwRect(rect)); }
このコード例では、フォールバック描画エンジンを使用してこのタスクを実行できることを示すために、ハードウェアアクセラレーションAPIが矩形ブレンドをサポートしていないことを想定しています。
そうでない場合は、色が設定され、矩形をブレンドするための呼び出しが発行されます。
DrawingEngine::blendRoundedRect は、角が丸い矩形がブレンドされるときに呼び出されます。また、プラットフォームの実装では、提供されたクリップ矩形がブレンドされる矩形よりも小さい場合、その矩形に対してクリップする必要があります。実装例を参照してください:
void ExampleDrawingEngine::blendRoundedRect(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Rect &rect, const PlatformInterface::Rect &clipRect, PlatformInterface::Rgba32 color, int radius, BlendMode blendMode) { // Implement rectangle blending here // HW_SetColor(1.0f, 1.0f, 1.0f, sourceOpacity * (1 / 256.0f)); // HW_SetClip(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); // HW_BlitRoundRect(toHwRect(rect), radius); // HW_SetClip(0, 0, screen->width(), screen->height()); }
同様に、画像とアルファマップをブレンドする場合、ハードウェアアクセラレーションAPIにテクスチャのレイアウトとデータを通知する必要があります。この例では、それを処理するbindTexture 関数があると仮定しています。この関数は、特定のハードウェアアクセラレーションAPIのニーズに従って、プラットフォームポートによって実装されなければなりません。
blendImage 、ソースとなる矩形、デスティネーションとなる点、ソースとなる不透明度を0から256の範囲で指定します。その実装は次のようになります:
void ExampleDrawingEngine::blendImage(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Point &pos, const PlatformInterface::Texture &source, const PlatformInterface::Rect &sourceRect, int sourceOpacity, BlendMode blendMode) { // Fall back to default CPU drawing engine for pixel formats that can't be // blended with hardware acceleration if (source.format() == Qul::PixelFormat_RGB332 || source.format() == PixelFormat_RLE_ARGB32 || source.format() == PixelFormat_RLE_ARGB32_Premultiplied || source.format() == PixelFormat_RLE_RGB32 || source.format() == PixelFormat_RLE_RGB888) { DrawingEngine::blendImage(drawingDevice, pos, source, sourceRect, sourceOpacity, blendMode); return; } // Implement image blending here // HW_SetBlendMode(toHwBlendMode(blendMode)); // bindTexture(source); // HW_SetColor(1.0f, 1.0f, 1.0f, sourceOpacity * (1 / 256.0f)); // if (drawingDevice->testRenderHint(PlatformInterface::RenderHint::...)) { // Take the render hint into account in this blending operation // HW_Set...() // } // const Rect destinationRect(pos, sourceRect.size()); // HW_BlendTexture(toHwRect(sourceRect), toHwRect(destinationRect)); }
次に、blendAlphaMap は非常によく似ています。主な違いは、テクスチャがピクセルフォーマットPixelFormat_Alpha1 またはPixelFormat_Alpha8 を持つということで、それぞれ不透明度を示すピクセルあたり1ビットまたは1バイトを意味します。各ピクセルについて、その不透明度の値は、blendMode に応じてブレンドまたはブ リットされる前に、与えられたcolor を乗算する必要があります。
void ExampleDrawingEngine::blendAlphaMap(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Point &pos, const PlatformInterface::Texture &source, const PlatformInterface::Rect &sourceRect, PlatformInterface::Rgba32 color, BlendMode blendMode) { // Implement alpha map blending here // HW_SetBlendMode(toHwBlendMode(blendMode)); // bindTexture(source); // const float inv = 1 / 255.0f; // HW_SetColor(color.red() * inv, color.green() * inv, color.blue() * inv, color.alpha() * inv); // const PlatformInterface::Rect destinationRect(pos, sourceRect.size()); // HW_BlendTexture(toHwRect(sourceRect), toHwRect(destinationRect)); }
次に、画像とアルファマップの変換ブレンドがある。プラットフォームがこの高速化をサポートしている場合、blendTransformedImage の実装は次のようになるかもしれない:
void ExampleDrawingEngine::blendTransformedImage(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Transform &transform, const PlatformInterface::RectF &destinationRect, const PlatformInterface::Texture &source, const PlatformInterface::RectF &sourceRect, const PlatformInterface::Rect &clipRect, int sourceOpacity, BlendMode blendMode) { // Fall back to default CPU drawing engine for pixel formats that can't be // blended with hardware acceleration if (source.format() == Qul::PixelFormat_RGB332 || source.format() == PixelFormat_RLE_ARGB32 || source.format() == PixelFormat_RLE_ARGB32_Premultiplied || source.format() == PixelFormat_RLE_RGB32 || source.format() == PixelFormat_RLE_RGB888) { DrawingEngine::blendTransformedImage(drawingDevice, transform, destinationRect, source, sourceRect, clipRect, sourceOpacity, blendMode); return; } // Implement transformed image blending here // float matrix[16]; // toHwMatrix(transform, &matrix); // HW_SetTransformMatrix(matrix); // HW_SetClip(clipRect.x(), clipRect.y(), clipRect.width(), clipRect.height()); // HW_SetBlendMode(toHwBlendMode(blendMode)); // bindTexture(source); // HW_SetColor(1.0f, 1.0f, 1.0f, sourceOpacity * (1 / 256.0f)); // const Qul::PlatformInterface::RenderHints &renderHints = drawingDevice->renderHints(); // if(renderHints.testFlag(PlatformInterface::RenderHint::...) { // Take the render hint into account in this blending operation // HW_Set...() // } // HW_BlendTexture(toHwRect(sourceRect), toHwRect(destinationRect)); // HW_SetClip(0, 0, screen->width(), screen->height()); // HW_SetTransformIdentity(); }
クリッピング矩形の設定に加えて、与えられた変換をハードウェアアクセラレーションAPIが受け付ける行列表現に変換する必要があります。4x4行列を使用する場合、変換はtoHwMatrix の例のようになります:
static void toHwMatrix(const PlatformInterface::Transform &transform, float *matrix) { matrix[0] = transform.m11(); matrix[1] = transform.m12(); matrix[2] = 0; matrix[3] = 0; matrix[4] = transform.m21(); matrix[5] = transform.m22(); matrix[6] = 0; matrix[7] = 0; matrix[8] = 0; matrix[9] = 0; matrix[10] = 1; matrix[11] = 0; matrix[12] = transform.dx(); matrix[13] = transform.dy(); matrix[14] = 0; matrix[15] = 1; }
blendTransformedAlphaMap サンプルコードはblendTransformedImage と非常によく似ていますが、上記のblendAlphaMap サンプルコードですでに示されているように、色を考慮に入れている点が異なります。blendTransformedAlphaMap サンプル・コードについてはplatform/boards/qt/example-baremetal/platform_context.cpp ファイルを参照してください。
CPUアクセスの同期化
ハードウェアブレンディングは多くの場合非同期で行われるため、CPUベースでフレームバッファの読み書きが行われる場合、Qt Quick UltraliteコアによってDrawingEngine::synchronizeForCpuAccess 。この関数は、保留中のすべてのブレンディングコマンドがフレームバッファに完全にコミットされたことを確認するために、ハードウェアアクセラレーションブレンディングユニットと同期する必要があります。以下は、ダミーのハードウェアアクセラレーションAPIでどのように見えるかです:
void ExampleDrawingEngine::synchronizeForCpuAccess(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Rect &rect) { // HW_SyncFramebufferForCpuAccess(); framebufferAccessedByCpu = true; unsigned char *backBuffer = framebuffer[backBufferIndex]; for (int i = 0; i < rect.height(); ++i) { unsigned char *pixels = backBuffer + (ScreenWidth * (rect.y() + i) + rect.x()) * BytesPerPixel; // CleanInvalidateDCache_by_Addr(pixels, rect.width() * BytesPerPixel); } }
ハードウェアによっては、関係する領域のデータキャッシュを無効にする必要がある場合もある。これにより、ブレンディング・ユニットによって行われる非同期書き込みが、CPUによって完全に見られるようになる。
また、synchronizeAfterCpuAccess 関数は、プラットフォームによっては有用である。CPUレンダリングフォールバックが使用される場合、ディスプレイコントローラからの非同期読み出しがメモリにアクセスできるようにする前に、データキャッシュを無効にします:
static bool framebufferAccessedByCpu = false; void synchronizeAfterCpuAccess(const PlatformInterface::Rect &rect) { if (framebufferAccessedByCpu) { unsigned char *backBuffer = framebuffer[backBufferIndex]; for (int i = 0; i < rect.height(); ++i) { unsigned char *pixels = backBuffer + (ScreenWidth * (rect.y() + i) + rect.x()) * BytesPerPixel; // CleanInvalidateDCache_by_Addr(pixels, rect.width() * BytesPerPixel); } } }
さらに、Qt Quick Ultraliteコアがテクスチャデータを動的に読み書きできるようにするために、さらに2つの関数を実装する必要があります。これらはPlatformContext::waitUntilAsyncReadFinished とPlatformContext::flushCachesForAsyncRead です。
それらの実装は次のようになります:
void ExamplePlatform::waitUntilAsyncReadFinished(const void * /*begin*/, const void * /*end*/) { // HW_SyncFramebufferForCpuAccess(); } void ExamplePlatform::flushCachesForAsyncRead(const void * /*addr*/, size_t /*length*/) { // CleanInvalidateDCache_by_Addr(const_cast<void *>(addr), length); }
waitUntilAsyncReadFinished は、与えられたメモリ領域から読み取る非同期操作が、リターンする前に終了していることを確認する必要があります。通常、このメモリ領域はテクスチャーデータを指し、上書きされます。ここでの最も単純なオプションは、保留中のテクスチャブレンディング操作がないことを確認するために、ハードウェアアクセラレートブレンディングユニットと同期することです。
flushCachesForAsyncRead は、CPUによって書き込まれたすべての変更がフラッシュされ、後続の非同期読み取りが正しい最新のメモリデータを取得できるようにします。与えられたハードウェア上でこれが必要な場合、利用可能なデータキャッシュを無効にするための呼び出しがなければなりません。
以上で、Qt Quick Ultralite UIを移植先のプラットフォームでスムーズかつ効率的に動作させるために、ハードウェアアクセラレーションによるブレンディングを実装するために必要なすべての部分を網羅しました。
レンダーヒント
レンダーヒントは、描画エンジンにブレンディング操作中に何を適用するかを指示します。例えば、アンチエイリアスを有効にすると、エンジンはエッジを滑らかにすることを期待します。
描画エンジンがサポートするレンダリングヒントを指定するには、supportedRenderHints()) メソッドを実装する必要があります。
PlatformInterface::RenderHints ExampleDrawingEngine::supportedRenderHints() const { // Indicate the rendering hints that this DrawingEngine supports: // return PlatformInterface::RenderHints(PlatformInterface::RenderHint::.., ...); return PlatformInterface::RenderHints(); }
描画エンジンは、DrawingDevice::testRenderHint() またはDrawingDevice::renderHints() を使用して、レンダリング操作のアクティブなヒントを照会します。
void ExampleDrawingEngine::blendImage(PlatformInterface::DrawingDevice *drawingDevice, const PlatformInterface::Point &pos, const PlatformInterface::Texture &source, const PlatformInterface::Rect &sourceRect, int sourceOpacity, BlendMode blendMode) { // Fall back to default CPU drawing engine for pixel formats that can't be // blended with hardware acceleration if (source.format() == Qul::PixelFormat_RGB332 || source.format() == PixelFormat_RLE_ARGB32 || source.format() == PixelFormat_RLE_ARGB32_Premultiplied || source.format() == PixelFormat_RLE_RGB32 || source.format() == PixelFormat_RLE_RGB888) { DrawingEngine::blendImage(drawingDevice, pos, source, sourceRect, sourceOpacity, blendMode); return; } // Implement image blending here // HW_SetBlendMode(toHwBlendMode(blendMode)); // bindTexture(source); // HW_SetColor(1.0f, 1.0f, 1.0f, sourceOpacity * (1 / 256.0f)); // if (drawingDevice->testRenderHint(PlatformInterface::RenderHint::...)) { // Take the render hint into account in this blending operation // HW_Set...() // } // const Rect destinationRect(pos, sourceRect.size()); // HW_BlendTexture(toHwRect(sourceRect), toHwRect(destinationRect)); }
DrawingDevice::fallbackDrawingEngine() は、描画エンジンがレンダリングヒントをサポートしていない場合のブレンドに使用されます。
Qul::PlatformInterface::DrawingEngine::supportedRenderHints()、Qul::PlatformInterface::DrawingDevice::renderHints()、Qul::PlatformInterface::DrawingDevice::setRenderHint()、Qul::PlatformInterface::DrawingDevice::testRenderHint()も参照 。
特定の Qt ライセンスの下で利用可能です。
詳細を確認してください。