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 に設定してください。
blend関数の実際の実装は、ハードウェアアクセラレーション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バイトが不透明度を表すことを意味します。各ピクセルについて、その不透明度値は、指定されたcolor で乗算された後、blendMode に応じてブレンドまたはブリット処理されます。
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が受け入れるような行列表現に変換する必要があります。4×4行列を使用する場合、その変換は例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 のサンプルコードは、前述のblendAlphaMap のサンプルコードで示したように、色を考慮している点を除けば、blendTransformedImage と非常に似ています。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() は、DrawingEngine がレンダリングヒントをサポートしていない場合のブレンドに使用されます。
Qul::PlatformInterface::DrawingEngine::supportedRenderHints()、Qul::PlatformInterface::DrawingDevice::renderHints()、Qul::PlatformInterface::DrawingDevice::setRenderHint()、Qul::PlatformInterface::DrawingDevice::testRenderHint()も参照してください 。
特定のQtライセンスの下で利用可能です。
詳細はこちら。